Merge "Revert "Use out pipBounds to save reentry size""
diff --git a/Android.bp b/Android.bp
index ae717f1..afdd832 100644
--- a/Android.bp
+++ b/Android.bp
@@ -374,7 +374,7 @@
 java_library {
     name: "framework-updatable-stubs-module_libs_api",
     static_libs: [
-        "framework-appsearch-stubs", // TODO: Update to module_libs_api when there is one.
+        "framework-appsearch.stubs.module_lib",
         "framework-graphics.stubs.module_lib",
         "framework-media.stubs.module_lib",
         "framework-mediaprovider.stubs.module_lib",
@@ -393,7 +393,7 @@
     installable: false,
     static_libs: [
         "framework-minus-apex",
-        "framework-appsearch",
+        "framework-appsearch.impl",
         "framework-graphics.impl",
         "framework-mediaprovider.impl",
         "framework-permission.impl",
@@ -624,8 +624,7 @@
     static_libs: [
         "app-compat-annotations",
         "framework-minus-apex",
-        // TODO(b/146218515): should be removed
-        "framework-appsearch",
+        "framework-appsearch.impl", // TODO(b/146218515): should be removed
         "framework-updatable-stubs-module_libs_api",
     ],
     sdk_version: "core_platform",
@@ -797,7 +796,6 @@
     name: "framework-services-net-module-wifi-shared-srcs",
     srcs: [
         "core/java/android/net/DhcpResults.java",
-        "core/java/android/net/util/IpUtils.java",
         "core/java/android/util/LocalLog.java",
     ],
 }
diff --git a/StubLibraries.bp b/StubLibraries.bp
index be24bd7..7c44f16 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -70,6 +70,7 @@
         "android.hardware.thermal-V2.0-java",
         "android.hardware.tv.input-V1.0-java-constants",
         "android.hardware.tv.tuner-V1.0-java-constants",
+        "android.hardware.tv.tuner-V1.1-java-constants",
         "android.hardware.usb-V1.0-java-constants",
         "android.hardware.usb-V1.1-java-constants",
         "android.hardware.usb.gadget-V1.0-java",
@@ -142,12 +143,6 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/public/api",
-        dest: "android.txt",
-    },
-    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -209,12 +204,6 @@
             baseline_file: "api/system-lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/system/api",
-        dest: "android.txt",
-    },
-    jdiff_enabled: true,
 }
 
 droidstubs {
@@ -246,7 +235,11 @@
     arg_files: [
         "core/res/AndroidManifest.xml",
     ],
-    args: metalava_framework_docs_args + " --show-annotation android.annotation.TestApi",
+    args: metalava_framework_docs_args
+        + " --show-annotation android.annotation.TestApi"
+        + " --show-for-stub-purposes-annotation android.annotation.SystemApi\\("
+        +     "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS"
+        + "\\)",
     check_api: {
         current: {
             api_file: "api/test-current.txt",
@@ -294,11 +287,6 @@
             baseline_file: "api/module-lib-lint-baseline.txt",
         },
     },
-    dist: {
-        targets: ["sdk", "win_sdk"],
-        dir: "apistubs/android/module-lib/api",
-        dest: "android.txt",
-    },
 }
 
 droidstubs {
@@ -367,6 +355,7 @@
     srcs: [ ":api-stubs-docs-non-updatable" ],
     static_libs: [
         "conscrypt.module.public.api.stubs",
+        "framework-appsearch.stubs",
         "framework-graphics.stubs",
         "framework-media.stubs",
         "framework-mediaprovider.stubs",
@@ -412,7 +401,7 @@
     srcs: [ ":system-api-stubs-docs-non-updatable" ],
     static_libs: [
         "conscrypt.module.public.api.stubs",
-        "framework-appsearch-stubs", // TODO: standardize appsearch stubs
+        "framework-appsearch.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index 321f471..e10fb07 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -21,60 +21,17 @@
     path: "java",
 }
 
-java_library {
+java_sdk_library {
     name: "framework-appsearch",
-    installable: true,
+    srcs: [ ":framework-appsearch-sources" ],
     sdk_version: "core_platform", // TODO(b/146218515) should be module_current
-    srcs: [":framework-appsearch-sources"],
-    hostdex: true, // for hiddenapi check
-    libs: [
-        "framework-minus-apex",  // TODO(b/146218515) should be removed
-    ],
+    impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed
     static_libs: ["icing-java-proto-lite"],
-    visibility: [
-        // TODO(b/146218515) remove this when framework is built with the stub of appsearch
-        "//frameworks/base",
-        "//frameworks/base/apex/appsearch:__subpackages__",
-    ],
-    jarjar_rules: "jarjar-rules.txt",
+    defaults: ["framework-module-defaults"],
     permitted_packages: ["android.app.appsearch"],
+    jarjar_rules: "jarjar-rules.txt",
+    aidl: {
+        include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed
+    },
     apex_available: ["com.android.appsearch"],
 }
-
-metalava_appsearch_docs_args =
-    "--hide-package com.android.server " +
-    "--error UnhiddenSystemApi " +
-    "--hide RequiresPermission " +
-    "--hide MissingPermission " +
-    "--hide BroadcastBehavior " +
-    "--hide HiddenSuperclass " +
-    "--hide DeprecationMismatch " +
-    "--hide UnavailableSymbol " +
-    "--hide SdkConstant " +
-    "--hide HiddenTypeParameter " +
-    "--hide Todo --hide Typo " +
-    "--hide HiddenTypedefConstant " +
-    "--show-annotation android.annotation.SystemApi "
-
-droidstubs {
-    name: "framework-appsearch-stubs-srcs",
-    srcs: [":framework-appsearch-sources"],
-    libs: ["framework-annotations-lib"],
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
-    args: metalava_appsearch_docs_args,
-    sdk_version: "module_current",
-}
-
-java_library {
-    name: "framework-appsearch-stubs",
-    srcs: [":framework-appsearch-stubs-srcs"],
-    aidl: {
-        export_include_dirs: [
-            "java",
-        ],
-    },
-    sdk_version: "module_current",
-    installable: false,
-}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/module-lib-current.txt b/apex/appsearch/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/module-lib-current.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/module-lib-removed.txt b/apex/appsearch/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/removed.txt b/apex/appsearch/framework/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/apex/appsearch/framework/api/system-current.txt b/apex/appsearch/framework/api/system-current.txt
new file mode 100644
index 0000000..4a6194e
--- /dev/null
+++ b/apex/appsearch/framework/api/system-current.txt
@@ -0,0 +1,9 @@
+// Signature format: 2.0
+package android.app.appsearch {
+
+  public class AppSearchManagerFrameworkInitializer {
+    method public static void initialize();
+  }
+
+}
+
diff --git a/apex/appsearch/framework/api/system-removed.txt b/apex/appsearch/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/apex/appsearch/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/api/Android.bp b/api/Android.bp
index f0218b8..388bb68 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -48,6 +48,7 @@
     name: "frameworks-base-api-current-merged.txt",
     srcs: [
         ":conscrypt.module.public.api{.public.api.txt}",
+        ":framework-appsearch{.public.api.txt}",
         ":framework-graphics{.public.api.txt}",
         ":framework-media{.public.api.txt}",
         ":framework-mediaprovider{.public.api.txt}",
@@ -61,12 +62,19 @@
     out: ["current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/public/api",
+        dest: "android.txt",
+    },
 }
 
 genrule {
     name: "frameworks-base-api-removed-merged.txt",
     srcs: [
         ":conscrypt.module.public.api{.public.removed-api.txt}",
+        ":framework-appsearch{.public.removed-api.txt}",
+        ":framework-graphics{.public.removed-api.txt}",
         ":framework-media{.public.removed-api.txt}",
         ":framework-mediaprovider{.public.removed-api.txt}",
         ":framework-permission{.public.removed-api.txt}",
@@ -84,6 +92,7 @@
 genrule {
     name: "frameworks-base-api-system-current-merged.txt",
     srcs: [
+        ":framework-appsearch{.system.api.txt}",
         ":framework-graphics{.system.api.txt}",
         ":framework-media{.system.api.txt}",
         ":framework-mediaprovider{.system.api.txt}",
@@ -97,11 +106,18 @@
     out: ["system-current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system/api",
+        dest: "android.txt",
+    },
 }
 
 genrule {
     name: "frameworks-base-api-system-removed-merged.txt",
     srcs: [
+        ":framework-appsearch{.system.removed-api.txt}",
+        ":framework-graphics{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
         ":framework-mediaprovider{.system.removed-api.txt}",
         ":framework-permission{.system.removed-api.txt}",
@@ -119,6 +135,7 @@
 genrule {
     name: "frameworks-base-api-module-lib-current-merged.txt",
     srcs: [
+        ":framework-appsearch{.module-lib.api.txt}",
         ":framework-graphics{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
         ":framework-mediaprovider{.module-lib.api.txt}",
@@ -132,11 +149,18 @@
     out: ["module-lib-current.txt"],
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/module-lib/api",
+        dest: "android.txt",
+    },
 }
 
 genrule {
     name: "frameworks-base-api-module-lib-removed-merged.txt",
     srcs: [
+        ":framework-appsearch{.module-lib.removed-api.txt}",
+        ":framework-graphics{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
         ":framework-mediaprovider{.module-lib.removed-api.txt}",
         ":framework-permission{.module-lib.removed-api.txt}",
diff --git a/api/current.txt b/api/current.txt
index 83df1d9..cdc2ab67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25456,6 +25456,7 @@
 
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
+    method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
     method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
     method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
@@ -26408,7 +26409,7 @@
     method public boolean containsKey(String);
     method public int describeContents();
     method public android.graphics.Bitmap getBitmap(String);
-    method @IntRange(from=0) public int getBitmapDimensionLimit();
+    method @IntRange(from=1) public int getBitmapDimensionLimit();
     method @NonNull public android.media.MediaDescription getDescription();
     method public long getLong(String);
     method public android.media.Rating getRating(String);
@@ -26458,7 +26459,7 @@
     method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
     method public android.media.MediaMetadata.Builder putString(String, String);
     method public android.media.MediaMetadata.Builder putText(String, CharSequence);
-    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
   }
 
   @Deprecated public abstract class MediaMetadataEditor {
@@ -44100,11 +44101,13 @@
     method public long getLastAudiblyAlertedMillis();
     method public String getOverrideGroupKey();
     method public int getRank();
+    method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
     method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
     method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isConversation();
     method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -48464,7 +48467,7 @@
     method @Deprecated public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -48487,7 +48490,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
-    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+    method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method @Deprecated public boolean iccCloseLogicalChannel(int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 2d24d5b..b8b6687 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,10 +1,20 @@
 // Signature format: 2.0
 package android.app {
 
+  public class ActivityManager {
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+  }
+
   public class AppOpsManager {
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
+  }
+
   public class NotificationManager {
     method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
     field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+    field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+  }
+
   @Deprecated public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
     ctor @Deprecated public MediaParceledListSlice(@NonNull java.util.List<T>);
     method @Deprecated public int describeContents();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2919f13..1404f7e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -38,6 +38,7 @@
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+    field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
@@ -120,6 +121,7 @@
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -4617,6 +4619,58 @@
 
 }
 
+package android.media.musicrecognition {
+
+  public class MusicRecognitionManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION) public void beginStreamingSearch(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.musicrecognition.MusicRecognitionManager.RecognitionCallback);
+    field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7
+    field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1
+    field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5
+    field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static interface MusicRecognitionManager.RecognitionCallback {
+    method public void onAudioStreamClosed();
+    method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int);
+    method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public abstract class MusicRecognitionService extends android.app.Service {
+    ctor public MusicRecognitionService();
+    method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
+  }
+
+  public static interface MusicRecognitionService.Callback {
+    method public void onRecognitionFailed(int);
+    method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public final class RecognitionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method @NonNull public android.media.AudioFormat getAudioFormat();
+    method public int getCaptureSession();
+    method public int getIgnoreBeginningFrames();
+    method public int getMaxAudioLengthSeconds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR;
+  }
+
+  public static final class RecognitionRequest.Builder {
+    ctor public RecognitionRequest.Builder();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest build();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int);
+  }
+
+}
+
 package android.media.session {
 
   public final class MediaSessionManager {
@@ -11500,6 +11554,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -11530,6 +11585,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -11580,6 +11636,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
diff --git a/api/test-current.txt b/api/test-current.txt
index a27268d..85be4ec 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -23,6 +23,7 @@
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
+    field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
@@ -78,20 +79,22 @@
   }
 
   public class ActivityManager {
+    method @RequiresPermission("android.permission.SET_ACTIVITY_WATCHER") public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
     method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
     method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
     method public long getTotalRam();
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
-    method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
     method public static boolean isHighEndGfx();
     method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
+    method @RequiresPermission("android.permission.SET_ACTIVITY_WATCHER") public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
     method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
     method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors();
     method public static void resumeAppSwitches() throws android.os.RemoteException;
     method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int);
-    method @RequiresPermission("android.permission.MANAGE_USERS") public boolean switchUser(@NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle);
     field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7
     field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1
     field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6
@@ -200,12 +203,12 @@
   public class AppOpsManager {
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
-    method @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
-    method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
+    method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
+    method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static int getNumOps();
     method public static String[] getOpStrs();
-    method @NonNull @RequiresPermission("android.permission.GET_APP_OPS_STATS") public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
+    method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
     method public boolean isOperationActive(int, int, String);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long);
     method public static int opToDefaultMode(@NonNull String);
@@ -451,10 +454,15 @@
   }
 
   public class DreamManager {
-    method @RequiresPermission("android.permission.READ_DREAM_STATE") public boolean isDreaming();
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName);
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName);
-    method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream();
+    method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isDreaming();
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void setActiveDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void startDream(@NonNull android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream();
+  }
+
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
   }
 
   public final class NotificationChannel implements android.os.Parcelable {
@@ -546,14 +554,14 @@
   }
 
   public class UiModeManager {
-    method @RequiresPermission("android.permission.ENTER_CAR_MODE_PRIORITIZED") public void enableCarMode(@IntRange(from=0) int, int);
+    method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int);
     method public boolean isNightModeLocked();
     method public boolean isUiModeLocked();
   }
 
   public class WallpaperManager {
     method @Nullable public android.graphics.Bitmap getBitmap();
-    method @RequiresPermission("android.permission.SET_WALLPAPER_COMPONENT") public boolean setWallpaperComponent(android.content.ComponentName);
+    method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName);
     method public boolean shouldEnableWideColorGamut();
     method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int);
   }
@@ -629,11 +637,11 @@
 package android.app.backup {
 
   public class BackupManager {
-    method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getConfigurationIntent(String);
-    method @RequiresPermission("android.permission.BACKUP") public android.content.Intent getDataManagementIntent(String);
-    method @Nullable @RequiresPermission("android.permission.BACKUP") public CharSequence getDataManagementIntentLabel(@NonNull String);
-    method @Deprecated @Nullable @RequiresPermission("android.permission.BACKUP") public String getDataManagementLabel(@NonNull String);
-    method @RequiresPermission("android.permission.BACKUP") public String getDestinationString(String);
+    method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getConfigurationIntent(String);
+    method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getDataManagementIntent(String);
+    method @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public CharSequence getDataManagementIntentLabel(@NonNull String);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public String getDataManagementLabel(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String);
   }
 
 }
@@ -758,21 +766,21 @@
   }
 
   public class RoleControllerManager {
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
   }
 
   public final class RoleManager {
-    method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
     method @Nullable public String getSmsRoleHolder(int);
-    method @RequiresPermission("android.permission.OBSERVE_ROLE_HOLDERS") public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
     field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
@@ -879,7 +887,7 @@
     method public int getUserId();
     method public void setAutofillOptions(@Nullable android.content.AutofillOptions);
     method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions);
-    method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
     field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
     field public static final String BUGREPORT_SERVICE = "bugreport";
     field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
@@ -898,7 +906,7 @@
   }
 
   public class Intent implements java.lang.Cloneable android.os.Parcelable {
-    field @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
+    field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
     field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED";
     field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID";
     field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
@@ -998,7 +1006,7 @@
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
     method public void setEnableRollback(boolean);
     method public void setEnableRollback(boolean, int);
-    method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
     method public void setInstallAsInstantApp(boolean);
     method public void setInstallerPackageName(@Nullable String);
@@ -1010,25 +1018,25 @@
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
     method @Nullable public String getContentCaptureServicePackageName();
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract String getDefaultBrowserPackageNameAsUser(int);
+    method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int);
     method @Nullable public String getDefaultTextClassifierPackageName();
     method @Nullable public String getIncidentReportApproverPackageName();
     method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
-    method @NonNull @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method @Nullable public abstract String[] getNamesForUids(int[]);
     method @NonNull public abstract String getPermissionControllerPackageName();
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
     method @Nullable public String getSystemTextClassifierPackageName();
     method @Nullable public String getWellbeingPackageName();
-    method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int);
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, int, int, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
+    method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
@@ -1223,6 +1231,35 @@
 
 }
 
+package android.hardware.biometrics {
+
+  public class BiometricManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession();
+  }
+
+  public class BiometricTestSession implements java.lang.AutoCloseable {
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateReject(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateSuccess(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void close();
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enableTestHal(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollFinish(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollStart(int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void internalCleanup(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyAcquired(int, int);
+    method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyError(int, int);
+  }
+
+  public class SensorProperties {
+    method public int getSensorId();
+    method public int getSensorStrength();
+    field public static final int STRENGTH_CONVENIENCE = 0; // 0x0
+    field public static final int STRENGTH_STRONG = 2; // 0x2
+    field public static final int STRENGTH_WEAK = 1; // 0x1
+  }
+
+}
+
 package android.hardware.camera2 {
 
   public abstract class CameraDevice implements java.lang.AutoCloseable {
@@ -1313,7 +1350,7 @@
   }
 
   public final class DisplayManager {
-    method @RequiresPermission("android.permission.ACCESS_AMBIENT_LIGHT_STATS") public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
     method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
@@ -1328,11 +1365,19 @@
 
 }
 
+package android.hardware.fingerprint {
+
+  @Deprecated public class FingerprintManager {
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession();
+  }
+
+}
+
 package android.hardware.hdmi {
 
   public final class HdmiControlManager {
     method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient();
-    method @RequiresPermission("android.permission.HDMI_CEC") public void setStandbyMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean);
     field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE";
     field public static final int AVR_VOLUME_MUTED = 101; // 0x65
     field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2
@@ -1443,11 +1488,9 @@
     field public static final int PORT_OUTPUT = 1; // 0x1
   }
 
-  public class HdmiSwitchClient {
+  public class HdmiSwitchClient extends android.hardware.hdmi.HdmiClient {
     method public int getDeviceType();
     method @NonNull public java.util.List<android.hardware.hdmi.HdmiPortInfo> getPortInfo();
-    method public void sendKeyEvent(int, boolean);
-    method public void sendVendorCommand(int, byte[], boolean);
   }
 
 }
@@ -1718,8 +1761,7 @@
     method @NonNull public String[] getBackgroundThrottlingWhitelist();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
     method @NonNull public String[] getIgnoreSettingsWhitelist();
-    method @Deprecated @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
-    method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -1740,7 +1782,7 @@
   }
 
   public static final class LocationRequest.Builder {
-    method @NonNull @RequiresPermission("android.permission.UPDATE_APP_OPS_STATS") public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
@@ -1775,12 +1817,12 @@
   }
 
   public class AudioManager {
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method public boolean hasRegisteredDynamicPolicy();
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission("android.permission.MODIFY_AUDIO_ROUTING") public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
     field public static final int SUCCESS = 0; // 0x0
   }
 
@@ -2035,6 +2077,57 @@
 
 }
 
+package android.media.musicrecognition {
+
+  public class MusicRecognitionManager {
+    field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7
+    field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1
+    field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5
+    field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static interface MusicRecognitionManager.RecognitionCallback {
+    method public void onAudioStreamClosed();
+    method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int);
+    method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public abstract class MusicRecognitionService extends android.app.Service {
+    ctor public MusicRecognitionService();
+    method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
+  }
+
+  public static interface MusicRecognitionService.Callback {
+    method public void onRecognitionFailed(int);
+    method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public final class RecognitionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method @NonNull public android.media.AudioFormat getAudioFormat();
+    method public int getCaptureSession();
+    method public int getIgnoreBeginningFrames();
+    method public int getMaxAudioLengthSeconds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR;
+  }
+
+  public static final class RecognitionRequest.Builder {
+    ctor public RecognitionRequest.Builder();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest build();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int);
+  }
+
+}
+
 package android.media.tv {
 
   public final class TvInputManager {
@@ -2222,15 +2315,15 @@
     method @NonNull public android.net.NetworkCapabilities build();
     method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
     method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
     method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
     method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
     method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setOwnerUid(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setRequestorUid(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP") public android.net.NetworkCapabilities.Builder setSignalStrength(int);
-    method @NonNull @RequiresPermission("android.permission.NETWORK_FACTORY") public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+    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 setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
@@ -2308,11 +2401,11 @@
 
   public class TetheringManager {
     method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
     field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
     field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
     field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
@@ -2377,9 +2470,9 @@
   public static class TetheringManager.TetheringRequest.Builder {
     ctor public TetheringManager.TetheringRequest.Builder(int);
     method @NonNull public android.net.TetheringManager.TetheringRequest build();
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
+    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
   }
 
   public class TrafficStats {
@@ -2581,7 +2674,7 @@
 package android.os {
 
   public class BatteryManager {
-    method @RequiresPermission("android.permission.POWER_SAVER") public boolean setChargingStateUpdateDelayMillis(int);
+    method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setChargingStateUpdateDelayMillis(int);
   }
 
   public final class BugreportManager {
@@ -2625,7 +2718,7 @@
   }
 
   public class DeviceIdleManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void endIdle(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void endIdle(@NonNull String);
     method @NonNull public String[] getSystemPowerWhitelist();
     method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
   }
@@ -2878,21 +2971,21 @@
   }
 
   public final class PowerManager {
-    method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveModeTrigger();
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
-    method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSaveHint(boolean, int);
-    method @RequiresPermission(anyOf={"android.permission.DEVICE_POWER", "android.permission.POWER_SAVER"}) public boolean setPowerSaveModeEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger();
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
+    method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
     field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
     field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
     field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
   }
 
   public class PowerWhitelistManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull String);
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
-    method @RequiresPermission("android.permission.DEVICE_POWER") public void removeFromWhitelist(@NonNull String);
-    method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
-    method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
+    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);
+    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull 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
@@ -2958,8 +3051,8 @@
   }
 
   public class SystemConfigManager {
-    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
-    method @NonNull @RequiresPermission("android.permission.READ_CARRIER_APP_INFO") public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
   }
 
   public class SystemProperties {
@@ -2988,7 +3081,7 @@
   }
 
   public class UserManager {
-    method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
     method public static boolean isSplitSystemUser();
     field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
   }
@@ -3048,10 +3141,10 @@
   }
 
   public abstract class Vibrator {
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public boolean isVibrating();
-    method @RequiresPermission("android.permission.ACCESS_VIBRATOR_STATE") public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener);
   }
 
   public static interface Vibrator.OnVibratorStateChangedListener {
@@ -3150,12 +3243,12 @@
 
   public class DynamicSystemClient {
     ctor public DynamicSystemClient(@NonNull android.content.Context);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void bind();
+    method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void bind();
     method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
     method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void start(@NonNull android.net.Uri, long, long);
-    method @RequiresPermission("android.permission.INSTALL_DYNAMIC_SYSTEM") public void unbind();
+    method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long, long);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void unbind();
     field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
     field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
     field public static final int CAUSE_ERROR_IO = 3; // 0x3
@@ -3209,13 +3302,13 @@
 package android.permission {
 
   public final class PermissionControllerManager {
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
-    method @RequiresPermission("android.permission.GET_RUNTIME_PERMISSIONS") public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
-    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.RESTORE_RUNTIME_PERMISSIONS"}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>);
+    method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle);
     field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1
     field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2
     field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
@@ -3236,9 +3329,9 @@
   }
 
   public final class PermissionManager {
-    method @IntRange(from=0) @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
+    method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
     method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
-    method @RequiresPermission(anyOf={"android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY", android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
   }
 
   public static final class PermissionManager.SplitPermissionInfo {
@@ -3304,14 +3397,14 @@
   }
 
   public final class DeviceConfig {
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(@NonNull String, @NonNull String, float);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(@NonNull String, @NonNull String, int);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(@NonNull String, @NonNull String, long);
-    method @NonNull @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(@NonNull String, @NonNull String);
-    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(@NonNull String, @NonNull String, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(@NonNull String, @NonNull String, long);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String);
     method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException;
@@ -3399,6 +3492,11 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
     field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
+    field public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability";
+    field public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode";
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 3; // 0x3
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 1; // 0x1
+    field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 2; // 0x2
     field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
     field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
     field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
@@ -4168,8 +4266,8 @@
     method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
-    field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
+    field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
   }
 
   public final class PreciseDataConnectionState implements android.os.Parcelable {
@@ -4210,27 +4308,31 @@
   public class TelephonyManager {
     method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method public int checkCarrierPrivilegesForPackage(String);
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
+    method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
     method public int getCarrierIdListVersion();
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
-    method @Nullable @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public android.content.ComponentName getDefaultRespondViaMessageApplication();
+    method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication();
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void resetOtaEmergencyNumberDbFilePath();
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
     method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>);
-    method @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
+    method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor);
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
   }
 
@@ -5306,7 +5408,7 @@
   }
 
   public interface WindowManager extends android.view.ViewManager {
-    method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
+    method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public default void holdLock(int);
     method public default void setShouldShowIme(int, boolean);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5327,11 +5429,11 @@
 
   public final class AccessibilityManager {
     method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler);
-    method @NonNull @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void performAccessibilityShortcut();
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void registerSystemAction(@NonNull android.app.RemoteAction, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int);
     method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
-    method @RequiresPermission("android.permission.MANAGE_ACCESSIBILITY") public void unregisterSystemAction(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int);
   }
 
   public static interface AccessibilityManager.AccessibilityServicesStateChangeListener {
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 91a09e3..a314702 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2530,6 +2530,16 @@
 NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
+
+NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
     
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 90c8788..e5dbb42 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -266,7 +266,7 @@
                 147 [(module) = "framework", (module) = "statsd"];
         BiometricSystemHealthIssueDetected biometric_system_health_issue_detected =
                 148 [(module) = "framework"];
-        BubbleUIChanged bubble_ui_changed = 149 [(module) = "sysui"];
+        BubbleUIChanged bubble_ui_changed = 149 [(module) = "framework"];
         ScheduledJobConstraintChanged scheduled_job_constraint_changed =
                 150 [(module) = "framework"];
         BluetoothActiveDeviceChanged bluetooth_active_device_changed =
@@ -493,6 +493,8 @@
         WifiConnectionStateChanged wifi_connection_state_changed = 308 [(module) = "wifi"];
         HdmiCecActiveSourceChanged hdmi_cec_active_source_changed = 309 [(module) = "framework"];
         HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"];
+        AirplaneMode airplane_mode = 311 [(module) = "telephony"];
+        ModemRestart modem_restart = 312 [(module) = "telephony"];
 
         // StatsdStats tracks platform atoms with ids upto 500.
         // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value.
@@ -4466,6 +4468,7 @@
         // which requires reboot and not eligible for any reboot promotion strategy
         // (e.g. soft restart, notification restart).
         NO_REBOOT_PROMOTION_STRATEGY_ELIGIBLE = 30;
+        REBOOT_TRIGGER_FAILURE = 31;
     }
     optional State state = 6;
     // Possible experiment ids for monitoring this push.
@@ -5266,12 +5269,23 @@
  * Event to track Jank for various system interactions.
  *
  * Logged from:
- *  frameworks/base/core/java/android/os/aot/FrameTracker.java
+ *  frameworks/base/core/java/com/android/internal/jank/FrameTracker.java
  */
 message UIInteractionFrameInfoReported {
     enum InteractionType {
         UNKNOWN = 0;
         NOTIFICATION_SHADE_SWIPE = 1;
+        SHADE_EXPAND_COLLAPSE_LOCK = 2;
+        SHADE_SCROLL_FLING = 3;
+        SHADE_ROW_EXPAND = 4;
+        SHADE_ROW_SWIPE = 5;
+        SHADE_QS_EXPAND_COLLAPSE = 6;
+        SHADE_QS_SCROLL_SWIPE = 7;
+        LAUNCHER_APP_LAUNCH_FROM_RECENTS = 8;
+        LAUNCHER_APP_LAUNCH_FROM_ICON = 9;
+        LAUNCHER_APP_CLOSE_TO_HOME = 10;
+        LAUNCHER_APP_CLOSE_TO_PIP = 11;
+        LAUNCHER_QUICK_SWITCH = 12;
     }
 
     optional InteractionType interaction_type = 1;
@@ -10575,12 +10589,48 @@
 }
 
 /**
+ * Push information about usage of airplane mode.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
+ */
+message AirplaneMode {
+    // Status of airplane mode
+    optional bool is_enabled = 1;
+
+    // When is_enabled is false, indicates if this was a very short airplane mode toggle
+    // (i.e. airplane mode was disabled after less than 10 seconds from enablement).
+    optional bool short_toggle = 2;
+
+    // Carrier ID of the SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 3;
+}
+
+/**
+ * Push information about modem restarts.
+ *
+ * Logged from:
+ *   frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
+ */
+message ModemRestart {
+    // Software version of the modem, as provided by android.os.Build.getRadioVersion().
+    optional string baseband_version = 1;
+
+    // Reason of the modem restart, as provided in the modemReset indication of IRadio HAL.
+    optional string reason = 2;
+
+    // Carrier ID of the first SIM card.
+    // See https://source.android.com/devices/tech/config/carrierid.
+    optional int32 carrier_id = 3;
+}
+
+/**
  * Logs gnss stats from location service provider
  *
  * Pulled from:
  *  frameworks/base/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
  */
-
 message GnssStats {
     // Number of location reports since boot
     optional int64 location_reports = 1;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 9dda248..2a37b58 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,11 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "../guardrail/StatsdStats.h"
 #include "GaugeMetricProducer.h"
-#include "../stats_log_util.h"
+
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
@@ -154,6 +156,58 @@
     }
 }
 
+bool GaugeMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const GaugeMetric& metric = config.gauge_metric(configIndex);
+    // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mWhatMatcherIndex)) {
+        return false;
+    }
+
+    // Need to update maps since the index changed, but mTriggerAtomId will not change.
+    int triggerTrackerIndex;
+    if (metric.has_trigger_event() &&
+        !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                              /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                              newAtomMatchingTrackerMap, trackerToMetricMap,
+                                              triggerTrackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+    mEventMatcherWizard = matcherWizard;
+    return true;
+}
+
 void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
     if (mCurrentSlicedBucket == nullptr ||
         mCurrentSlicedBucket->size() == 0) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index e933d4b..9bdaac9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -53,8 +53,8 @@
 // This gauge metric producer first register the puller to automatically pull the gauge at the
 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the guage at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+// producer always reports the gauge at the earliest time of the bucket when the condition is met.
+class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
 public:
     GaugeMetricProducer(
             const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
@@ -142,7 +142,23 @@
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
-    const int mWhatMatcherIndex;
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
+    int mWhatMatcherIndex;
 
     sp<EventMatcherWizard> mEventMatcherWizard;
 
@@ -209,6 +225,8 @@
 
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4360010..18e62d2 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -568,6 +568,7 @@
     FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
 };
 
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 5ff6975..93795d4 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -531,10 +531,69 @@
             return false;
         }
     }
-    // TODO: determine update status for count, gauge, value, duration metrics.
+
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        set<int64_t> matcherDependencies;
+        matcherDependencies.insert(metric.what());
+        if (metric.has_trigger_event()) {
+            matcherDependencies.insert(metric.trigger_event());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies,
+                    conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+                    metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    // TODO: determine update status for value, duration metrics.
     return true;
 }
 
+// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
+// and calls onConfigUpdated to update all indices.
+optional<sp<MetricProducer>> updateMetric(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& oldMetricProducerMap,
+        const vector<sp<MetricProducer>>& oldMetricProducers,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+    if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+        ALOGE("Could not find Metric %lld in the previous config, but expected it "
+              "to be there",
+              (long long)metricId);
+        return nullopt;
+    }
+    const int oldIndex = oldMetricProducerIt->second;
+    sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+    if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
+                                   oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+                                   matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
+                                   metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                                   activationAtomTrackerToMetricMap,
+                                   deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return nullopt;
+    }
+    return {producer};
+}
+
 bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
                    const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
                    const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
@@ -588,41 +647,29 @@
     // Now, perform the update. Must iterate the metric types in the same order
     int metricIndex = 0;
     for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
-        newMetricProducerMap[config.count_metric(i).id()] = metricIndex;
         const CountMetric& metric = config.count_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
         switch (metricsToUpdate[metricIndex]) {
             case UPDATE_PRESERVE: {
-                const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
-                if (oldMetricProducerIt == oldMetricProducerMap.end()) {
-                    ALOGE("Could not find Metric %lld in the previous config, but expected it "
-                          "to be there",
-                          (long long)metric.id());
-                    return false;
-                }
-                const int oldIndex = oldMetricProducerIt->second;
-                sp<MetricProducer> producer = oldMetricProducers[oldIndex];
-                producer->onConfigUpdated(
-                        config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
-                        newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
-                        conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                newMetricProducers.push_back(producer);
                 break;
             }
             case UPDATE_REPLACE:
             case UPDATE_NEW: {
-                sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata(
+                producer = createCountMetricProducerAndUpdateMetadata(
                         key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
                         allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
                         conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
                         allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                if (producer == nullptr) {
-                    return false;
-                }
-                newMetricProducers.push_back(producer);
                 break;
             }
             default: {
@@ -631,42 +678,34 @@
                 return false;
             }
         }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
     }
     for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
         newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
         const EventMetric& metric = config.event_metric(i);
+        optional<sp<MetricProducer>> producer;
         switch (metricsToUpdate[metricIndex]) {
             case UPDATE_PRESERVE: {
-                const auto& oldMetricProducerIt = oldMetricProducerMap.find(metric.id());
-                if (oldMetricProducerIt == oldMetricProducerMap.end()) {
-                    ALOGE("Could not find Metric %lld in the previous config, but expected it "
-                          "to be there",
-                          (long long)metric.id());
-                    return false;
-                }
-                const int oldIndex = oldMetricProducerIt->second;
-                sp<MetricProducer> producer = oldMetricProducers[oldIndex];
-                producer->onConfigUpdated(
-                        config, i, metricIndex, allAtomMatchingTrackers, oldAtomMatchingTrackerMap,
-                        newAtomMatchingTrackerMap, matcherWizard, allConditionTrackers,
-                        conditionTrackerMap, wizard, metricToActivationMap, trackerToMetricMap,
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                newMetricProducers.push_back(producer);
                 break;
             }
             case UPDATE_REPLACE:
             case UPDATE_NEW: {
-                sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+                producer = createEventMetricProducerAndUpdateMetadata(
                         key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
                         newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
                         initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
                         conditionToMetricMap, activationAtomTrackerToMetricMap,
                         deactivationAtomTrackerToMetricMap, metricsWithActivation);
-                if (producer == nullptr) {
-                    return false;
-                }
-                newMetricProducers.push_back(producer);
                 break;
             }
             default: {
@@ -675,8 +714,49 @@
                 return false;
             }
         }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
     }
-    // TODO: perform update for count, gauge, value, duration metric.
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                producer = createGaugeMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                        metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                        activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                        metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    // TODO: perform update for value, duration metric.
 
     const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
                                           config.whitelisted_atom_ids().end());
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index 98c6222..34e265c 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -348,7 +348,7 @@
     return true;
 }
 
-sp<MetricProducer> createCountMetricProducerAndUpdateMetadata(
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -366,14 +366,14 @@
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
         ALOGW("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
-        return nullptr;
+        return nullopt;
     }
     int trackerIndex;
     if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
                                               metric.has_dimensions_in_what(),
                                               allAtomMatchingTrackers, atomMatchingTrackerMap,
                                               trackerToMetricMap, trackerIndex)) {
-        return nullptr;
+        return nullopt;
     }
 
     int conditionIndex = -1;
@@ -381,12 +381,12 @@
         if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
                                         metric.links(), allConditionTrackers, conditionIndex,
                                         conditionToMetricMap)) {
-            return nullptr;
+            return nullopt;
         }
     } else {
         if (metric.links_size() > 0) {
             ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-            return nullptr;
+            return nullopt;
         }
     }
 
@@ -395,12 +395,12 @@
     if (metric.slice_by_state_size() > 0) {
         if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
                                     allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-            return nullptr;
+            return nullopt;
         }
     } else {
         if (metric.state_link_size() > 0) {
             ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
-            return nullptr;
+            return nullopt;
         }
     }
 
@@ -410,20 +410,20 @@
                                 atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
                                 deactivationAtomTrackerToMetricMap, metricsWithActivation,
                                 eventActivationMap, eventDeactivationMap)) {
-        return nullptr;
+        return nullopt;
     }
 
     uint64_t metricHash;
     if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
-        return nullptr;
+        return nullopt;
     }
 
-    return new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                   metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
-                                   eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+    return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+                                    eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
 }
 
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -439,13 +439,13 @@
         vector<int>& metricsWithActivation) {
     if (!metric.has_id() || !metric.has_what()) {
         ALOGW("cannot find the metric name or what in config");
-        return nullptr;
+        return nullopt;
     }
     int trackerIndex;
     if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
                                               allAtomMatchingTrackers, atomMatchingTrackerMap,
                                               trackerToMetricMap, trackerIndex)) {
-        return nullptr;
+        return nullopt;
     }
 
     int conditionIndex = -1;
@@ -453,12 +453,12 @@
         if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
                                         metric.links(), allConditionTrackers, conditionIndex,
                                         conditionToMetricMap)) {
-            return nullptr;
+            return nullopt;
         }
     } else {
         if (metric.links_size() > 0) {
             ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-            return nullptr;
+            return nullopt;
         }
     }
 
@@ -472,12 +472,125 @@
 
     uint64_t metricHash;
     if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
-        return nullptr;
+        return nullopt;
     }
 
-    return new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                   metricHash, timeBaseNs, eventActivationMap,
-                                   eventDeactivationMap);
+    return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, eventActivationMap,
+                                    eventDeactivationMap)};
+}
+
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGW("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+
+    if ((!metric.gauge_fields_filter().has_include_all() ||
+         (metric.gauge_fields_filter().include_all() == false)) &&
+        !hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+    if ((metric.gauge_fields_filter().has_include_all() &&
+         metric.gauge_fields_filter().include_all() == true) &&
+        hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    // For GaugeMetric atom, it should be simple matcher with one tagId.
+    if (atomMatcher->getAtomIds().size() != 1) {
+        return nullopt;
+    }
+    int atomTagId = *(atomMatcher->getAtomIds().begin());
+    int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+    int triggerTrackerIndex;
+    int triggerAtomId = -1;
+    if (metric.has_trigger_event()) {
+        if (pullTagId == -1) {
+            ALOGW("Pull atom not specified for trigger");
+            return nullopt;
+        }
+        // trigger_event should be used with FIRST_N_SAMPLES
+        if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
+            ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
+            return nullopt;
+        }
+        if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                                  /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                                  atomMatchingTrackerMap, trackerToMetricMap,
+                                                  triggerTrackerIndex)) {
+            return nullopt;
+        }
+        sp<AtomMatchingTracker> triggerAtomMatcher =
+                allAtomMatchingTrackers.at(triggerTrackerIndex);
+        triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
+    }
+
+    if (!metric.has_trigger_event() && pullTagId != -1 &&
+        metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
+        ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, trackerIndex, matcherWizard, pullTagId,
+                                    triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
+                                    pullerManager, eventActivationMap, eventDeactivationMap)};
 }
 
 bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
@@ -628,17 +741,17 @@
         int metricIndex = allMetricProducers.size();
         const CountMetric& metric = config.count_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata(
+        optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
                 key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
                 allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
                 conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
                 allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
                 metricsWithActivation);
-        if (producer == nullptr) {
+        if (!producer) {
             return false;
         }
-        allMetricProducers.push_back(producer);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build DurationMetricProducer
@@ -762,16 +875,16 @@
         int metricIndex = allMetricProducers.size();
         const EventMetric& metric = config.event_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        sp<MetricProducer> producer = createEventMetricProducerAndUpdateMetadata(
+        optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
                 key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
                 atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
                 initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
                 conditionToMetricMap, activationAtomTrackerToMetricMap,
                 deactivationAtomTrackerToMetricMap, metricsWithActivation);
-        if (producer == nullptr) {
+        if (!producer) {
             return false;
         }
-        allMetricProducers.push_back(producer);
+        allMetricProducers.push_back(producer.value());
     }
 
     // build ValueMetricProducer
@@ -871,104 +984,20 @@
 
     // Gauge metrics.
     for (int i = 0; i < config.gauge_metric_size(); i++) {
-        const GaugeMetric& metric = config.gauge_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
-        if ((!metric.gauge_fields_filter().has_include_all() ||
-             (metric.gauge_fields_filter().include_all() == false)) &&
-            !hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-        if ((metric.gauge_fields_filter().has_include_all() &&
-             metric.gauge_fields_filter().include_all() == true) &&
-            hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-
         int metricIndex = allMetricProducers.size();
+        const GaugeMetric& metric = config.gauge_metric(i);
         metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
-                                                  metric.has_dimensions_in_what(),
-                                                  allAtomMatchingTrackers, atomMatchingTrackerMap,
-                                                  trackerToMetricMap, trackerIndex)) {
-            return false;
-        }
-
-        sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
-        // For GaugeMetric atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getAtomIds().size() != 1) {
-            return false;
-        }
-        int atomTagId = *(atomMatcher->getAtomIds().begin());
-        int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
-        int triggerTrackerIndex;
-        int triggerAtomId = -1;
-        if (metric.has_trigger_event()) {
-            if (pullTagId == -1) {
-                ALOGW("Pull atom not specified for trigger");
-                return false;
-            }
-            // event_trigger should be used with FIRST_N_SAMPLES
-            if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
-                return false;
-            }
-            if (!handleMetricWithAtomMatchingTrackers(
-                        metric.trigger_event(), metricIndex, /*enforceOneAtom=*/true,
-                        allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap,
-                        triggerTrackerIndex)) {
-                return false;
-            }
-            sp<AtomMatchingTracker> triggerAtomMatcher =
-                    allAtomMatchingTrackers.at(triggerTrackerIndex);
-            triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
-        }
-
-        if (!metric.has_trigger_event() && pullTagId != -1 &&
-            metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
-            ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap,
+        optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        uint64_t metricHash;
-        if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+                metricsWithActivation);
+        if (!producer) {
             return false;
         }
-
-        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
-                trackerIndex, matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs,
-                currentTimeNs, pullerManager, eventActivationMap, eventDeactivationMap);
-        allMetricProducers.push_back(gaugeProducer);
+        allMetricProducers.push_back(producer.value());
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
         const auto no_report_metric = config.no_report_metric(i);
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index f6b2b7d..f909aff 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -94,8 +94,8 @@
         std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
 
 // Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or null if there was an error.
-sp<MetricProducer> createCountMetricProducerAndUpdateMetadata(
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -113,8 +113,8 @@
         std::vector<int>& metricsWithActivation);
 
 // Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
-// the appropriate indices. Returns an sp to the producer, or null if there was an error.
-sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -129,6 +129,25 @@
         std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
         std::vector<int>& metricsWithActivation);
 
+// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
 // Helper functions for MetricsManager to initialize from StatsdConfig.
 // *Note*: only initStatsdConfig() should be called from outside.
 // All other functions are intermediate
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 9e675878..60705cf 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -27,6 +27,7 @@
 #include "src/condition/CombinationConditionTracker.h"
 #include "src/condition/SimpleConditionTracker.h"
 #include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/GaugeMetricProducer.h"
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "tests/statsd_test_util.h"
 
@@ -137,6 +138,23 @@
     return metric;
 }
 
+GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
+                              optional<int64_t> condition, optional<int64_t> triggerEvent) {
+    GaugeMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    metric.set_sampling_type(samplingType);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    if (triggerEvent) {
+        metric.set_trigger_event(triggerEvent.value());
+    }
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    return metric;
+}
+
 }  // anonymous namespace
 
 TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1266,6 +1284,120 @@
     EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
 }
 
+TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change split bucket on app upgrade, which should change the proto, causing replacement.
+    config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) {
+    StatsdConfig config;
+    AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = triggerEvent;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
     StatsdConfig config;
 
@@ -1727,6 +1859,221 @@
     EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
 }
 
+TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few gauge metrics.
+    // Will be preserved.
+    GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher1Id);
+    int64_t gauge1Id = gauge1.id();
+    *config.add_gauge_metric() = gauge1;
+
+    // Will be replaced.
+    GaugeMetric gauge2 =
+            createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+    int64_t gauge2Id = gauge2.id();
+    *config.add_gauge_metric() = gauge2;
+
+    // Will be replaced.
+    GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           nullopt, matcher3Id);
+    int64_t gauge3Id = gauge3.id();
+    *config.add_gauge_metric() = gauge3;
+
+    // Will be replaced.
+    GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
+                                           predicate1Id, nullopt);
+    int64_t gauge4Id = gauge4.id();
+    *config.add_gauge_metric() = gauge4;
+
+    // Will be deleted.
+    GaugeMetric gauge5 =
+            createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
+    int64_t gauge5Id = gauge5.id();
+    *config.add_gauge_metric() = gauge5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<EventMatcherWizard> oldMatcherWizard =
+            static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+    // Change gauge2, causing it to be replaced.
+    gauge2.set_max_num_gauge_atoms_per_bucket(50);
+
+    // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
+    set<int64_t> replacedMatchers = {matcher3Id};
+
+    // New gauge metric.
+    GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher3Id);
+    int64_t gauge6Id = gauge6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Say that predicate1 is unknown since the initial condition never changed.
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_gauge_metric() = gauge6;
+    const int gauge6Index = 0;
+    *newConfig.add_gauge_metric() = gauge3;
+    const int gauge3Index = 1;
+    *newConfig.add_gauge_metric() = gauge1;
+    const int gauge1Index = 2;
+    *newConfig.add_gauge_metric() = gauge4;
+    const int gauge4Index = 3;
+    *newConfig.add_gauge_metric() = gauge2;
+    const int gauge2Index = 4;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
+            {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    GaugeMetricProducer* gaugeProducer1 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
+    EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
+    EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
+    GaugeMetricProducer* gaugeProducer2 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
+    EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
+    EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
+    GaugeMetricProducer* gaugeProducer3 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
+    EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
+    EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
+    GaugeMetricProducer* gaugeProducer4 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
+    EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
+    EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
+    GaugeMetricProducer* gaugeProducer6 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
+    EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
+    EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
+
+    sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
+    EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+    EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
     StatsdConfig config;
     // Add atom matchers
@@ -1866,6 +2213,10 @@
     int64_t matcher2Id = matcher2.id();
     *config.add_atom_matcher() = matcher2;
 
+    AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
     Predicate predicate1 = CreateScreenIsOnPredicate();
     int64_t predicate1Id = predicate1.id();
     *config.add_predicate() = predicate1;
@@ -1881,24 +2232,35 @@
     int64_t eventMetricId = eventMetric.id();
     *config.add_event_metric() = eventMetric;
 
+    // Will be replaced because the definition changes - a predicate is added.
+    GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
+                                                GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+    int64_t gaugeMetricId = gaugeMetric.id();
+    *config.add_gauge_metric() = gaugeMetric;
+
     EXPECT_TRUE(initConfig(config));
 
     // Used later to ensure the condition wizard is replaced. Get it before doing the update.
     sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
-    EXPECT_EQ(oldConditionWizard->getStrongCount(), 3);
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 4);
 
     // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
     set<int64_t> replacedMatchers;
     replacedMatchers.insert(matcher2Id);
 
+    // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
+    gaugeMetric.set_condition(predicate1Id);
+
     // Map the matchers and predicates in reverse order to force the indices to change.
     std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
-    const int matcher2Index = 0;
-    newAtomMatchingTrackerMap[matcher2Id] = 0;
-    const int matcher1Index = 1;
-    newAtomMatchingTrackerMap[matcher1Id] = 1;
+    const int matcher3Index = 0;
+    newAtomMatchingTrackerMap[matcher3Id] = 0;
+    const int matcher2Index = 1;
+    newAtomMatchingTrackerMap[matcher2Id] = 1;
+    const int matcher1Index = 2;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
     // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
-    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(2);
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
     std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
                       newAtomMatchingTrackers.begin());
 
@@ -1916,6 +2278,8 @@
     const int countMetricIndex = 0;
     *newConfig.add_event_metric() = eventMetric;
     const int eventMetricIndex = 1;
+    *newConfig.add_gauge_metric() = gaugeMetric;
+    const int gaugeMetricIndex = 2;
 
     // Output data structures to validate.
     unordered_map<int64_t, int> newMetricProducerMap;
@@ -1939,29 +2303,34 @@
     unordered_map<int64_t, int> expectedMetricProducerMap = {
             {countMetricId, countMetricIndex},
             {eventMetricId, eventMetricIndex},
+            {gaugeMetricId, gaugeMetricIndex},
     };
     EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
 
     // Make sure preserved metrics are the same.
-    ASSERT_EQ(newMetricProducers.size(), 2);
+    ASSERT_EQ(newMetricProducers.size(), 3);
     EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
               newMetricProducers[newMetricProducerMap.at(countMetricId)]);
 
     // Make sure replaced metrics are different.
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
               newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
+              newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
 
     // Verify the conditionToMetricMap.
     ASSERT_EQ(conditionToMetricMap.size(), 1);
     const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
-    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex));
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex, gaugeMetricIndex));
 
     // Verify the trackerToMetricMap.
-    ASSERT_EQ(trackerToMetricMap.size(), 2);
+    ASSERT_EQ(trackerToMetricMap.size(), 3);
     const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
     EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex));
     const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
     EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex));
 
     // Verify event activation/deactivation maps.
     ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
@@ -1975,10 +2344,13 @@
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
     EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
 
     sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
     EXPECT_NE(newConditionWizard, oldConditionWizard);
-    EXPECT_EQ(newConditionWizard->getStrongCount(), 3);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 4);
     oldMetricProducers.clear();
     // Only reference to the old wizard should be the one in the test.
     EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e75d2f6..30efb48 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -100,6 +100,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.Executor;
 
 /**
  * <p>
@@ -4752,31 +4753,43 @@
     }
 
     /**
-     * Register with {@link HomeVisibilityObserver} with ActivityManager.
-     * TODO: b/144351078 expose as SystemApi
+     * Register to be notified when the visibility of the home screen changes.
+     *
+     * @param executor The executor on which the listener should be called.
+     * @param listener The listener that is called when home visibility changes.
      * @hide
      */
-    public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
-        Preconditions.checkNotNull(observer);
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+    public void addHomeVisibilityListener(@NonNull Executor executor,
+            @NonNull HomeVisibilityListener listener) {
+        Preconditions.checkNotNull(listener);
+        Preconditions.checkNotNull(executor);
         try {
-            observer.init(mContext, this);
-            getService().registerProcessObserver(observer.mObserver);
+            listener.init(mContext, executor, this);
+            getService().registerProcessObserver(listener.mObserver);
             // Notify upon first registration.
-            observer.onHomeVisibilityChanged(observer.mIsHomeActivityVisible);
+            executor.execute(() ->
+                    listener.onHomeVisibilityChanged(listener.mIsHomeActivityVisible));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Unregister with {@link HomeVisibilityObserver} with ActivityManager.
-     * TODO: b/144351078 expose as SystemApi
+     * Removes a listener that was previously added with {@link #addHomeVisibilityListener}.
+     *
+     * @param listener The listener that was previously added.
      * @hide
      */
-    public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) {
-        Preconditions.checkNotNull(observer);
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+    public void removeHomeVisibilityListener(@NonNull HomeVisibilityListener listener) {
+        Preconditions.checkNotNull(listener);
         try {
-            getService().unregisterProcessObserver(observer.mObserver);
+            getService().unregisterProcessObserver(listener.mObserver);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1f8cf8a..89cb8b8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -103,6 +103,11 @@
             IBinder whitelistToken, long duration);
 
     /**
+     * Returns the flags set for a {@link PendingIntent}.
+     */
+    public abstract int getPendingIntentFlags(IIntentSender target);
+
+    /**
      * Allows a {@link PendingIntent} to start activities from background.
      */
     public abstract void setPendingIntentAllowBgActivityStarts(
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 85cb120..3748a33 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1284,10 +1284,10 @@
     }
 
     /**
-     * Sets the id of the display where activity should be launched.
-     * An app can launch activities on public displays or private displays that are owned by the app
-     * or where an app already has activities. Otherwise, trying to launch on a private display
-     * or providing an invalid display id will result in an exception.
+     * Sets the id of the display where the activity should be launched.
+     * An app can launch activities on public displays or displays where the app already has
+     * activities. Otherwise, trying to launch on a private display or providing an invalid display
+     * id will result in an exception.
      * <p>
      * Setting launch display id will be ignored on devices that don't have
      * {@link android.content.pm.PackageManager#FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS}.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d1fbd76..c5bc356 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -7337,21 +7337,48 @@
     }
 
     /**
-     * Make note of an application performing an operation.  Note that you must pass
-     * in both the uid and name of the application to be checked; this function will verify
-     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
+     * Make note of an application performing an operation and check if the application is allowed
+     * to perform it.
      *
      * <p>If this is a check that is not preceding the protected operation, use
      * {@link #unsafeCheckOp} instead.
      *
+     * <p>The identity of the package the app-op is noted for is specified by the
+     * {@code uid} and {@code packageName} parameters. If this is noted for a regular app both
+     * should be set and the package needs to be part of the uid. In the very rare case that an
+     * app-op is noted for an entity that does not have a package name, the package can be
+     * {@code null}. As it is possible that a single process contains more than one package the
+     * {@code packageName} should be {@link Context#getPackageName() read} from the context of the
+     * caller of the API (in the app process) that eventually triggers this check. If this op is
+     * not noted for a running process the {@code packageName} cannot be read from the context, but
+     * it should be clear which package the note is for.
+     *
+     * <p>If the  {@code uid} and {@code packageName} do not match this return
+     * {@link #MODE_IGNORED}.
+     *
+     * <p>Beside the access check this method also records the access. While the access check is
+     * based on {@code uid} and/or {@code packageName} the access recording is done based on the
+     * {@code packageName} and {@code attributionTag}. The {@code attributionTag} should be
+     * {@link Context#getAttributionTag() read} from the same context the package name is read from.
+     * In the case the check is not related to an API call, the  {@code attributionTag} should be
+     * {@code null}. Please note that e.g. registering a callback for later is still an API call and
+     * the code should store the attribution tag along the package name for being used in this
+     * method later.
+     *
+     * <p>The {@code message} parameter only needs to be set when this method is <ul>not</ul>
+     * called in a two-way binder call from the client. In this case the message is a free form text
+     * that is meant help the app developer determine what part of the app's code triggered the
+     * note. This message is passed back to the app in the
+     * {@link OnOpNotedCallback#onAsyncNoted(AsyncNotedAppOp)} callback. A good example of a useful
+     * message is including the {@link System#identityHashCode(Object)} of the listener that will
+     * receive data or the name of the manifest-receiver.
+     *
      * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
+     * @param uid The uid of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @param attributionTag The {@link Context#createAttributionContext attribution tag} of the
+     *                       calling context or {@code null} for default attribution
+     * @param message A message describing why the op was noted
      *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
      * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
@@ -7359,33 +7386,16 @@
      *
      * @throws SecurityException If the app has been configured to crash on this op.
      */
+    // For platform callers of this method, please read the package name parameter from
+    // Context#getOpPackageName.
+    // When noting a callback, the message can be computed using the #toReceiverId method.
     public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
             @Nullable String attributionTag, @Nullable String message) {
         return noteOp(strOpToOp(op), uid, packageName, attributionTag, message);
     }
 
     /**
-     * Make note of an application performing an operation.  Note that you must pass
-     * in both the uid and name of the application to be checked; this function will verify
-     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
-     *
-     * <p>If this is a check that is not preceding the protected operation, use
-     * {@link #unsafeCheckOp} instead.
-     *
-     * @param op The operation to note.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     *
-     * @throws SecurityException If the app has been configured to crash on this op.
+     * @see #noteOp(String, int, String, String, String
      *
      * @hide
      */
@@ -7423,16 +7433,7 @@
      * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
      * {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #noteOp(String, int, String, String, String)
      */
     public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
             @Nullable String attributionTag, @Nullable String message) {
@@ -7440,19 +7441,7 @@
     }
 
     /**
-     * Like {@link #noteOp(String, int, String, String, String)} but instead of throwing a
-     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @param op The operation to note.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or {@code
-     * null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #noteOpNoThrow(String, int, String, String, String)
      *
      * @hide
      */
@@ -7509,23 +7498,7 @@
     }
 
     /**
-     * Make note of an application performing an operation on behalf of another application when
-     * handling an IPC. This function will verify that the calling uid and proxied package name
-     * match, and if not, return {@link #MODE_IGNORED}. If this call succeeds, the last execution
-     * time of the operation for the proxied app and your app will be updated to the current time.
-     *
-     * @param op The operation to note. One of the OP_* constants.
-     * @param proxiedPackageName The name of the application calling into the proxy application.
-     * @param proxiedUid The uid of the proxied application
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
-     * if it is not allowed and should be silently ignored (without causing the app to crash).
-     *
-     * @throws SecurityException If the proxy or proxied app has been configured to crash on this
-     * op.
+     * @see #noteProxyOp(String, String, int, String, String)
      *
      * @hide
      */
@@ -7587,15 +7560,7 @@
      * Like {@link #noteProxyOp(String, String, int, String, String)} but instead
      * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * <p>This API requires package with the {@code proxiedPackageName} to belong to
-     * {@code proxiedUid}.
-     *
-     * @param op The op to note
-     * @param proxiedPackageName The package to note the op for
-     * @param proxiedUid The uid the package belongs to
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @see #noteOpNoThrow(String, int, String, String, String)
      */
     public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
             int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
@@ -7604,16 +7569,7 @@
     }
 
     /**
-     * Like {@link #noteProxyOp(int, String, int, String, String)} but instead
-     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @param op The op to note
-     * @param proxiedPackageName The package to note the op for or {@code null} if the op should be
-     *                           noted for the "android" package
-     * @param proxiedUid The uid the package belongs to
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted
+     * @see #noteProxyOpNoThrow(String, String, int, String, String)
      *
      * @hide
      */
@@ -7701,6 +7657,9 @@
     /**
      * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
      * returns {@link #MODE_ERRORED}.
+     *
+     * @see #checkOp(int, int, String)
+     *
      * @hide
      */
     @UnsupportedAppUsage
@@ -7832,6 +7791,10 @@
     /**
      * Report that an application has started executing a long-running operation.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link #noteOp(String, int, String, String, String)}
+     *
      * @param op The operation to start.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -7852,22 +7815,7 @@
     }
 
     /**
-     * Report that an application has started executing a long-running operation.
-     *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     *
-     * @throws SecurityException If the app has been configured to crash on this op or
-     * the package is not in the passed in UID.
+     * @see #startOp(String, int, String, String, String)
      *
      * @hide
      */
@@ -7913,16 +7861,7 @@
      * Like {@link #startOp(String, int, String, String, String)} but instead of throwing a
      * {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #startOp(String, int, String, String, String)
      */
     public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
             @NonNull String attributionTag, @Nullable String message) {
@@ -7930,20 +7869,7 @@
     }
 
     /**
-     * Like {@link #startOp(int, int, String, boolean, String, String)} but instead of throwing a
-     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @param attributionTag The {@link Context#createAttributionContext attribution tag} or
-     * {@code null} for default attribution
-     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
-     * @param message Description why op was started
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
+     * @see #startOpNoThrow(String, int, String, String, String)
      *
      * @hide
      */
@@ -7985,7 +7911,7 @@
      *
      * @param op The op to note
      * @param proxiedUid The uid to note the op for {@code null}
-     * @param proxiedUid The package name the uid belongs to
+     * @param proxiedPackageName The package name the uid belongs to
      * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
      * attribution tag} or {@code null} for default attribution
      * @param message A message describing the reason the op was noted
@@ -8013,17 +7939,7 @@
      *Like {@link #startProxyOp(String, int, String, String, String)} but instead
      * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
-     * @param op The op to note
-     * @param proxiedUid The uid to note the op for {@code null}
-     * @param proxiedUid The package name the uid belongs to
-     * @param proxiedAttributionTag The proxied {@link Context#createAttributionContext
-     * attribution tag} or {@code null} for default attribution
-     * @param message A message describing the reason the op was noted*
-     * <p>This API requires package with the {@code proxiedPackageName} to belong to
-     * {@code proxiedUid}.
-     *
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
-     * if it is not allowed and should be silently ignored (without causing the app to crash).
+     * @see #startProxyOp(String, int, String, String, String)
      */
     public int startProxyOpNoThrow(@NonNull String op, int proxiedUid,
             @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag,
@@ -8102,10 +8018,7 @@
     }
 
     /**
-     * Report that an application is no longer performing an operation that had previously
-     * been started with {@link #startOp(int, int, String, boolean, String, String)}. There is no
-     * validation of input or result; the parameters supplied here must be the exact same ones
-     * previously passed in when starting the operation.
+     * @see #finishOp(String, int, String, String)
      *
      * @hide
      */
diff --git a/core/java/android/app/HomeVisibilityListener.java b/core/java/android/app/HomeVisibilityListener.java
new file mode 100644
index 0000000..c6e5699
--- /dev/null
+++ b/core/java/android/app/HomeVisibilityListener.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.app;
+
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.Binder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener that will be invoked when the visibility of the home screen changes.
+ * Register this callback via {@link ActivityManager#addHomeVisibilityListener}
+ * @hide
+ */
+// This is a single-method listener that needs a bunch of supporting code, so it can't be an
+// interface
+@SuppressLint("ListenerInterface")
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+@TestApi
+public abstract class HomeVisibilityListener {
+    private Context mContext;
+    private ActivityManager mActivityManager;
+    private Executor mExecutor;
+    /** @hide */
+    android.app.IProcessObserver.Stub mObserver;
+    /** @hide */
+    boolean mIsHomeActivityVisible;
+
+    /** @hide */
+    void init(Context context, Executor executor, ActivityManager activityManager) {
+        mContext = context;
+        mActivityManager = activityManager;
+        mIsHomeActivityVisible = isHomeActivityVisible();
+        mExecutor = executor;
+    }
+
+    /**
+     * Called when the visibility of the home screen changes.
+     *
+     * @param isHomeActivityVisible Whether the home screen activity is now visible.
+     */
+    public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
+
+    public HomeVisibilityListener() {
+        mObserver = new android.app.IProcessObserver.Stub() {
+            @Override
+            public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
+                refreshHomeVisibility();
+            }
+
+            @Override
+            public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
+            }
+
+            @Override
+            public void onProcessDied(int pid, int uid) {
+                refreshHomeVisibility();
+            }
+
+            private void refreshHomeVisibility() {
+                boolean isHomeActivityVisible = isHomeActivityVisible();
+                if (mIsHomeActivityVisible != isHomeActivityVisible) {
+                    mIsHomeActivityVisible = isHomeActivityVisible;
+                    Binder.withCleanCallingIdentity(() ->
+                            mExecutor.execute(() ->
+                                    onHomeVisibilityChanged(mIsHomeActivityVisible)));
+                }
+            }
+        };
+    }
+
+    private boolean isHomeActivityVisible() {
+        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
+        if (tasks == null || tasks.isEmpty()) {
+            return false;
+        }
+
+        String top = tasks.get(0).topActivity.getPackageName();
+        if (top == null) {
+            return false;
+        }
+
+        // We can assume that the screen is idle if the home application is in the foreground.
+        String defaultHomePackage = mContext.getPackageManager()
+                .getHomeActivities(new ArrayList<>()).getPackageName();
+        if (Objects.equals(top, defaultHomePackage)) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityObserver.java
deleted file mode 100644
index 8422c6f..0000000
--- a/core/java/android/app/HomeVisibilityObserver.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import java.util.List;
-
-/**
- * An observer / callback to create and register by
- * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when
- * visibility of home page changes.
- * TODO: b/144351078 expose as SystemApi
- * @hide
- */
-public abstract class HomeVisibilityObserver {
-    private Context mContext;
-    private ActivityManager mActivityManager;
-    /** @hide */
-    IProcessObserver.Stub mObserver;
-    /** @hide */
-    boolean mIsHomeActivityVisible;
-
-    /** @hide */
-    void init(Context context, ActivityManager activityManager) {
-        mContext = context;
-        mActivityManager = activityManager;
-        mIsHomeActivityVisible = isHomeActivityVisible();
-    }
-
-    /**
-     * The API that needs implemented and will be triggered when activity on home page changes.
-     */
-    public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible);
-
-    public HomeVisibilityObserver() {
-        mObserver = new IProcessObserver.Stub() {
-            @Override
-            public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
-                boolean isHomeActivityVisible = isHomeActivityVisible();
-                if (mIsHomeActivityVisible != isHomeActivityVisible) {
-                    mIsHomeActivityVisible = isHomeActivityVisible;
-                    onHomeVisibilityChanged(mIsHomeActivityVisible);
-                }
-            }
-
-            @Override
-            public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
-            }
-
-            @Override
-            public void onProcessDied(int pid, int uid) {
-            }
-        };
-    }
-
-    private boolean isHomeActivityVisible() {
-        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
-        if (tasks == null || tasks.isEmpty()) {
-            return false;
-        }
-
-        String top = tasks.get(0).topActivity.getPackageName();
-        if (top == null) {
-            return false;
-        }
-
-        // We can assume that the screen is idle if the home application is in the foreground.
-        final Intent intent = new Intent(Intent.ACTION_MAIN, null);
-        intent.addCategory(Intent.CATEGORY_HOME);
-
-        ResolveInfo info = mContext.getPackageManager().resolveActivity(intent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        if (info != null && top.equals(info.activityInfo.packageName)) {
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 0d682d6..5438062 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2240,7 +2240,8 @@
                 .setTicker(tickerText)
                 .setContentTitle(contentTitle)
                 .setContentText(contentText)
-                .setContentIntent(PendingIntent.getActivity(context, 0, contentIntent, 0))
+                .setContentIntent(PendingIntent.getActivity(
+                        context, 0, contentIntent, PendingIntent.FLAG_MUTABLE))
                 .buildInto(this);
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 9100d57..90401ad 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -107,6 +107,8 @@
 import android.media.MediaTranscodeManager;
 import android.media.midi.IMidiManager;
 import android.media.midi.MidiManager;
+import android.media.musicrecognition.IMusicRecognitionManager;
+import android.media.musicrecognition.MusicRecognitionManager;
 import android.media.projection.MediaProjectionManager;
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
@@ -1119,6 +1121,17 @@
                 return new AutofillManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.MUSIC_RECOGNITION_SERVICE, MusicRecognitionManager.class,
+                new CachedServiceFetcher<MusicRecognitionManager>() {
+                    @Override
+                    public MusicRecognitionManager createService(ContextImpl ctx) {
+                        IBinder b = ServiceManager.getService(
+                                Context.MUSIC_RECOGNITION_SERVICE);
+                        return new MusicRecognitionManager(
+                                IMusicRecognitionManager.Stub.asInterface(b));
+                    }
+                });
+
         registerService(Context.CONTENT_CAPTURE_MANAGER_SERVICE, ContentCaptureManager.class,
                 new CachedServiceFetcher<ContentCaptureManager>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8a85b79..b74e18b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5718,8 +5718,8 @@
      * System apps can always bypass VPN.
      * <p> Note that the system doesn't update the allowlist when packages are installed or
      * uninstalled, the admin app must call this method to keep the list up to date.
-     * <p> When {@code lockdownEnabled} is false {@code lockdownWhitelist} is ignored . When
-     * {@code lockdownEnabled} is {@code true} and {@code lockdownWhitelist} is {@code null} or
+     * <p> When {@code lockdownEnabled} is false {@code lockdownAllowlist} is ignored . When
+     * {@code lockdownEnabled} is {@code true} and {@code lockdownAllowlist} is {@code null} or
      * empty, only system apps can bypass VPN.
      * <p> Setting always-on VPN package to {@code null} or using
      * {@link #setAlwaysOnVpnPackage(ComponentName, String, boolean)} clears lockdown allowlist.
@@ -5728,24 +5728,24 @@
      *         to remove an existing always-on VPN configuration
      * @param lockdownEnabled {@code true} to disallow networking when the VPN is not connected or
      *         {@code false} otherwise. This has no effect when clearing.
-     * @param lockdownWhitelist Packages that will be able to access the network directly when VPN
+     * @param lockdownAllowlist Packages that will be able to access the network directly when VPN
      *         is in lockdown mode but not connected. Has no effect when clearing.
      * @throws SecurityException if {@code admin} is not a device or a profile
      *         owner.
      * @throws NameNotFoundException if {@code vpnPackage} or one of
-     *         {@code lockdownWhitelist} is not installed.
+     *         {@code lockdownAllowlist} is not installed.
      * @throws UnsupportedOperationException if {@code vpnPackage} exists but does
      *         not support being set as always-on, or if always-on VPN is not
      *         available.
      */
     public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage,
-            boolean lockdownEnabled, @Nullable Set<String> lockdownWhitelist)
+            boolean lockdownEnabled, @Nullable Set<String> lockdownAllowlist)
             throws NameNotFoundException {
         throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 mService.setAlwaysOnVpnPackage(admin, vpnPackage, lockdownEnabled,
-                        lockdownWhitelist == null ? null : new ArrayList<>(lockdownWhitelist));
+                        lockdownAllowlist == null ? null : new ArrayList<>(lockdownAllowlist));
             } catch (ServiceSpecificException e) {
                 switch (e.errorCode) {
                     case ERROR_VPN_PACKAGE_NOT_FOUND:
@@ -5820,9 +5820,9 @@
         throwIfParentInstance("getAlwaysOnVpnLockdownWhitelist");
         if (mService != null) {
             try {
-                final List<String> whitelist =
-                        mService.getAlwaysOnVpnLockdownWhitelist(admin);
-                return whitelist == null ? null : new HashSet<>(whitelist);
+                final List<String> allowlist =
+                        mService.getAlwaysOnVpnLockdownAllowlist(admin);
+                return allowlist == null ? null : new HashSet<>(allowlist);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/FreezePeriod.java b/core/java/android/app/admin/FreezePeriod.java
index 657f017..eb6efec 100644
--- a/core/java/android/app/admin/FreezePeriod.java
+++ b/core/java/android/app/admin/FreezePeriod.java
@@ -39,8 +39,8 @@
 public class FreezePeriod {
     private static final String TAG = "FreezePeriod";
 
-    private static final int DUMMY_YEAR = 2001;
-    static final int DAYS_IN_YEAR = 365; // 365 since DUMMY_YEAR is not a leap year
+    private static final int SENTINEL_YEAR = 2001;
+    static final int DAYS_IN_YEAR = 365; // 365 since SENTINEL_YEAR is not a leap year
 
     private final MonthDay mStart;
     private final MonthDay mEnd;
@@ -60,9 +60,9 @@
      */
     public FreezePeriod(MonthDay start, MonthDay end) {
         mStart = start;
-        mStartDay = mStart.atYear(DUMMY_YEAR).getDayOfYear();
+        mStartDay = mStart.atYear(SENTINEL_YEAR).getDayOfYear();
         mEnd = end;
-        mEndDay = mEnd.atYear(DUMMY_YEAR).getDayOfYear();
+        mEndDay = mEnd.atYear(SENTINEL_YEAR).getDayOfYear();
     }
 
     /**
@@ -166,9 +166,9 @@
                 endYearAdjustment = 1;
             }
         }
-        final LocalDate startDate = LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).withYear(
+        final LocalDate startDate = LocalDate.ofYearDay(SENTINEL_YEAR, mStartDay).withYear(
                 now.getYear() + startYearAdjustment);
-        final LocalDate endDate = LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).withYear(
+        final LocalDate endDate = LocalDate.ofYearDay(SENTINEL_YEAR, mEndDay).withYear(
                 now.getYear() + endYearAdjustment);
         return new Pair<>(startDate, endDate);
     }
@@ -176,13 +176,13 @@
     @Override
     public String toString() {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd");
-        return LocalDate.ofYearDay(DUMMY_YEAR, mStartDay).format(formatter) + " - "
-                + LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).format(formatter);
+        return LocalDate.ofYearDay(SENTINEL_YEAR, mStartDay).format(formatter) + " - "
+                + LocalDate.ofYearDay(SENTINEL_YEAR, mEndDay).format(formatter);
     }
 
     /** @hide */
     private static MonthDay dayOfYearToMonthDay(int dayOfYear) {
-        LocalDate date = LocalDate.ofYearDay(DUMMY_YEAR, dayOfYear);
+        LocalDate date = LocalDate.ofYearDay(SENTINEL_YEAR, dayOfYear);
         return MonthDay.of(date.getMonth(), date.getDayOfMonth());
     }
 
@@ -191,7 +191,7 @@
      * @hide
      */
     private static int dayOfYearDisregardLeapYear(LocalDate date) {
-        return date.withYear(DUMMY_YEAR).getDayOfYear();
+        return date.withYear(SENTINEL_YEAR).getDayOfYear();
     }
 
     /**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1c7b617..60dce22 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -197,12 +197,12 @@
     void setCertInstallerPackage(in ComponentName who, String installerPackage);
     String getCertInstallerPackage(in ComponentName who);
 
-    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownWhitelist);
+    boolean setAlwaysOnVpnPackage(in ComponentName who, String vpnPackage, boolean lockdown, in List<String> lockdownAllowlist);
     String getAlwaysOnVpnPackage(in ComponentName who);
     String getAlwaysOnVpnPackageForUser(int userHandle);
     boolean isAlwaysOnVpnLockdownEnabled(in ComponentName who);
     boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle);
-    List<String> getAlwaysOnVpnLockdownWhitelist(in ComponentName who);
+    List<String> getAlwaysOnVpnLockdownAllowlist(in ComponentName who);
 
     void addPersistentPreferredActivity(in ComponentName admin, in IntentFilter filter, in ComponentName activity);
     void clearPackagePersistentPreferredActivities(in ComponentName admin, String packageName);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 666ba32..9c216a3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4502,6 +4502,14 @@
     public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
 
     /**
+     * Used to access {@link MusicRecognitionManagerService}.
+     *
+     * @hide
+     * @see #getSystemService(String)
+     */
+    public static final String MUSIC_RECOGNITION_SERVICE = "music_recognition";
+
+    /**
      * Official published name of the (internal) permission service.
      *
      * @see #getSystemService(String)
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index eec7c9c..159db92 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -116,6 +116,10 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code packageName}, {@code attributionTag}, and
+     * {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
      * @param pid The process id for which to check. Use {@link #PID_UNKNOWN} if the PID
@@ -262,11 +266,15 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
-     * @param packageName The package name making the IPC. If null the
+     * @param callingPackageName The package name making the IPC. If null the
      *     the first package for the calling UID will be used.
-     * @param attributionTag attribution tag
+     * @param callingAttributionTag attribution tag
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
      * @param message A message describing the reason the permission was checked
@@ -275,13 +283,13 @@
      */
     @PermissionResult
     public static int checkCallingPermissionForDataDelivery(@NonNull Context context,
-            @NonNull String permission, @Nullable String packageName,
-            @Nullable String attributionTag, @Nullable String message) {
+            @NonNull String permission, @Nullable String callingPackageName,
+            @Nullable String callingAttributionTag, @Nullable String message) {
         if (Binder.getCallingPid() == Process.myPid()) {
             return PERMISSION_HARD_DENIED;
         }
         return checkPermissionForDataDelivery(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName, attributionTag, message);
+                Binder.getCallingUid(), callingPackageName, callingAttributionTag, message);
     }
 
     /**
@@ -339,6 +347,10 @@
      * will evaluate the permission access based on the current fg/bg state of the app and
      * leave a record that the data was accessed.
      *
+     * <p>For more details how to determine the {@code callingPackageName},
+     * {@code callingAttributionTag}, and {@code message}, please check the description in
+     * {@link AppOpsManager#noteOp(String, int, String, String, String)}
+     *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 8d0cc68..35ef53b 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -16,14 +16,17 @@
 
 package android.hardware.biometrics;
 
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.os.RemoteException;
 import android.security.keystore.KeyGenParameterSpec;
@@ -196,6 +199,17 @@
     }
 
     /**
+     * Retrieves a test session for BiometricManager/BiometricPrompt.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public BiometricTestSession getTestSession() {
+        return null; // TODO(169459906)
+    }
+
+    /**
      * Determine if biometrics can be used. In other words, determine if
      * {@link BiometricPrompt} can be expected to be shown (hardware available, templates enrolled,
      * user-enabled). This is the equivalent of {@link #canAuthenticate(int)} with
diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java
new file mode 100644
index 0000000..719efa8
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricTestSession.java
@@ -0,0 +1,202 @@
+/*
+ * 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 android.hardware.biometrics;
+
+import static android.Manifest.permission.TEST_BIOMETRIC;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and
+ * {@link android.hardware.fingerprint.FingerprintManager}.
+ * @hide
+ */
+@TestApi
+public class BiometricTestSession implements AutoCloseable {
+
+    private static final String TAG = "TestManager";
+
+    private final Context mContext;
+    private final ITestService mTestService;
+
+    /**
+     * @hide
+     */
+    public BiometricTestSession(@NonNull Context context, @NonNull ITestService testService) {
+        mContext = context;
+        mTestService = testService;
+    }
+
+    /**
+     * @return A list of {@link SensorProperties}
+     */
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public List<SensorProperties> getSensorProperties() {
+        try {
+            final List<SensorPropertiesInternal> internalProps =
+                    mTestService.getSensorPropertiesInternal(mContext.getOpPackageName());
+            final List<SensorProperties> props = new ArrayList<>();
+            for (SensorPropertiesInternal internalProp : internalProps) {
+                props.add(new SensorProperties(internalProp.sensorId, internalProp.sensorStrength));
+            }
+            return props;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+     * any methods on the real HAL implementation. This allows the framework to test a substantial
+     * portion of the framework code that would otherwise require human interaction. Note that
+     * secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+     * equivalent for the secret key.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void enableTestHal(int sensorId, boolean enableTestHal) {
+        try {
+            mTestService.enableTestHal(sensorId, enableTestHal);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Starts the enrollment process. This should generally be used when the test HAL is enabled.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void enrollStart(int sensorId, int userId) {
+        try {
+            mTestService.enrollStart(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Finishes the enrollment process. Simulates the HAL's callback.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void enrollFinish(int sensorId, int userId) {
+        try {
+            mTestService.enrollFinish(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Simulates a successful authentication, but does not provide a valid HAT.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void authenticateSuccess(int sensorId, int userId) {
+        try {
+            mTestService.authenticateSuccess(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Simulates a rejected attempt.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void authenticateReject(int sensorId, int userId) {
+        try {
+            mTestService.authenticateReject(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Simulates an acquired message from the HAL.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void notifyAcquired(int sensorId, int userId) {
+        try {
+            mTestService.notifyAcquired(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Simulates an error message from the HAL.
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void notifyError(int sensorId, int userId) {
+        try {
+            mTestService.notifyError(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    /**
+     * Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+     * that isn't known by both sides are deleted. This should generally be used when the test
+     * HAL is disabled (e.g. to clean up after a test).
+     *
+     * @param sensorId Sensor that this command applies to.
+     * @param userId User that this command applies to.
+     */
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void internalCleanup(int sensorId, int userId) {
+        try {
+            mTestService.internalCleanup(sensorId, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Remote exception", e);
+        }
+    }
+
+    @Override
+    @RequiresPermission(TEST_BIOMETRIC)
+    public void close() {
+
+    }
+}
diff --git a/core/java/android/hardware/biometrics/ITestService.aidl b/core/java/android/hardware/biometrics/ITestService.aidl
new file mode 100644
index 0000000..6373132
--- /dev/null
+++ b/core/java/android/hardware/biometrics/ITestService.aidl
@@ -0,0 +1,57 @@
+/*
+ * 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 android.hardware.biometrics;
+
+import android.hardware.biometrics.SensorPropertiesInternal;
+
+/**
+ * A test service for FingerprintManager and BiometricPrompt.
+ * @hide
+ */
+interface ITestService {
+    // Returns a list of sensor properties supported by the interface.
+    List<SensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
+
+    // Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke
+    // any methods on the real HAL implementation. This allows the framework to test a substantial
+    // portion of the framework code that would otherwise require human interaction. Note that
+    // secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its
+    // equivalent for the secret key.
+    void enableTestHal(int sensorId, boolean enableTestHal);
+
+    // Starts the enrollment process. This should generally be used when the test HAL is enabled.
+    void enrollStart(int sensorId, int userId);
+
+    // Finishes the enrollment process. Simulates the HAL's callback.
+    void enrollFinish(int sensorId, int userId);
+
+    // Simulates a successful authentication, but does not provide a valid HAT.
+    void authenticateSuccess(int sensorId, int userId);
+
+    // Simulates a rejected attempt.
+    void authenticateReject(int sensorId, int userId);
+
+    // Simulates an acquired message from the HAL.
+    void notifyAcquired(int sensorId, int userId);
+
+    // Simulates an error message from the HAL.
+    void notifyError(int sensorId, int userId);
+
+    // Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment
+    // that isn't known by both sides are deleted. This should generally be used when the test
+    // HAL is disabled (e.g. to clean up after a test).
+    void internalCleanup(int sensorId, int userId);
+}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
index b3dcb8f..5b1b5e6 100644
--- a/core/java/android/hardware/biometrics/SensorProperties.java
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -25,19 +26,18 @@
  * The base class containing all modality-agnostic information.
  * @hide
  */
+@TestApi
 public class SensorProperties {
     /**
      * A sensor that meets the requirements for Class 1 biometrics as defined in the CDD. This does
      * not correspond to a public BiometricManager.Authenticators constant. Sensors of this strength
      * are not available to applications via the public API surface.
-     * @hide
      */
     public static final int STRENGTH_CONVENIENCE = 0;
 
     /**
      * A sensor that meets the requirements for Class 2 biometrics as defined in the CDD.
      * Corresponds to BiometricManager.Authenticators.BIOMETRIC_WEAK.
-     * @hide
      */
     public static final int STRENGTH_WEAK = 1;
 
@@ -46,7 +46,6 @@
      * Corresponds to BiometricManager.Authenticators.BIOMETRIC_STRONG.
      *
      * Notably, this is the only strength that allows generation of HardwareAuthToken(s).
-     * @hide
      */
     public static final int STRENGTH_STRONG = 2;
 
@@ -70,7 +69,6 @@
 
     /**
      * @return The sensor's unique identifier.
-     * @hide
      */
     public int getSensorId() {
         return mSensorId;
@@ -78,7 +76,6 @@
 
     /**
      * @return The sensor's strength.
-     * @hide
      */
     @Strength
     public int getSensorStrength() {
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
similarity index 80%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
index 71cd0a7..d85c788 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.hardware.biometrics;
 
-package android.media.tv;
-
-parcelable TvChannelInfo;
+parcelable SensorPropertiesInternal;
\ No newline at end of file
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index dda1890b..fc14b89 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware.display;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -381,6 +383,7 @@
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
+                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_INTERNAL);
                 }
                 return mTempDisplays.toArray(new Display[mTempDisplays.size()]);
             } finally {
@@ -401,6 +404,9 @@
     private void addPresentationDisplaysLocked(
             ArrayList<Display> displays, int[] displayIds, int matchType) {
         for (int i = 0; i < displayIds.length; i++) {
+            if (displayIds[i] == DEFAULT_DISPLAY) {
+                continue;
+            }
             Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
             if (display != null
                     && (display.getFlags() & Display.FLAG_PRESENTATION) != 0
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
index 57e52d9..9ce834ca 100644
--- a/core/java/android/hardware/fingerprint/Fingerprint.java
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -31,6 +31,10 @@
         mGroupId = groupId;
     }
 
+    public Fingerprint(CharSequence name, int fingerId, long deviceId) {
+        super(name, fingerId, deviceId);
+    }
+
     private Fingerprint(Parcel in) {
         super(in.readString(), in.readInt(), in.readLong());
         mGroupId = in.readInt();
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 23de303..b35a68f 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
@@ -28,6 +29,7 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -35,6 +37,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.BiometricTestSession;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.os.Binder;
 import android.os.CancellationSignal;
@@ -94,6 +97,22 @@
     private Fingerprint mRemovalFingerprint;
     private Handler mHandler;
 
+    /**
+     * Retrieves a test session for FingerprintManager.
+     * @hide
+     */
+    @TestApi
+    @NonNull
+    @RequiresPermission(TEST_BIOMETRIC)
+    public BiometricTestSession getTestSession() {
+        try {
+            return new BiometricTestSession(mContext,
+                    mService.getTestService(mContext.getOpPackageName()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private class OnEnrollCancelListener implements OnCancelListener {
         @Override
         public void onCancel() {
@@ -617,13 +636,24 @@
     }
 
     /**
-     * Finishes enrollment and cancels the current auth token.
+     * Revokes the current challenge.
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void revokeChallenge() {
+        // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1,
+        // this parameter is ignored.
+        revokeChallenge(0L);
+    }
+
+    /**
+     * Revokes the specified challenge.
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FINGERPRINT)
+    public void revokeChallenge(long challenge) {
         if (mService != null) try {
-            mService.revokeChallenge(mToken, mContext.getOpPackageName());
+            mService.revokeChallenge(mToken, mContext.getOpPackageName(), challenge);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -761,14 +791,14 @@
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         if (mService == null) {
             Slog.w(TAG, "onFingerDown: no fingerprint service");
             return;
         }
 
         try {
-            mService.onFingerDown(sensorId, x, y, minor, major);
+            mService.onPointerDown(sensorId, x, y, minor, major);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -778,14 +808,14 @@
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         if (mService == null) {
             Slog.w(TAG, "onFingerDown: no fingerprint service");
             return;
         }
 
         try {
-            mService.onFingerUp(sensorId);
+            mService.onPointerUp(sensorId);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index d5ce9e3..1f896cd 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -85,4 +85,9 @@
                 return false;
         }
     }
+
+    @Override
+    public String toString() {
+        return "ID: " + sensorId + ", Strength: " + sensorStrength + ", Type: " + sensorType;
+    }
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 7af7380..e8ca590 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
 
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.ITestService;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -30,6 +31,10 @@
  * @hide
  */
 interface IFingerprintService {
+
+    // Retrieves a test service
+    ITestService getTestService(String opPackageName);
+
     // Retrieve static sensor properties for all fingerprint sensors
     List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName);
 
@@ -91,7 +96,7 @@
     void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName);
 
     // Finish an enrollment sequence and invalidate the authentication token
-    void revokeChallenge(IBinder token, String opPackageName);
+    void revokeChallenge(IBinder token, String opPackageName, long challenge);
 
     // Determine if a user has at least one enrolled fingerprint
     boolean hasEnrolledFingerprints(int userId, String opPackageName);
@@ -121,10 +126,10 @@
     void initializeConfiguration(int sensorId, int strength);
 
     // Notifies about a finger touching the sensor area.
-    void onFingerDown(int sensorId, int x, int y, float minor, float major);
+    void onPointerDown(int sensorId, int x, int y, float minor, float major);
 
     // Notifies about a finger leaving the sensor area.
-    void onFingerUp(int sensorId);
+    void onPointerUp(int sensorId);
 
     // Sets the controller for managing the UDFPS overlay.
     void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index e21cb44..5877f1f 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -22,9 +22,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.net.util.IpUtils;
 import android.util.Log;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.InetAddress;
 
 /**
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
index 22288b6..c4f8fc2 100644
--- a/core/java/android/net/NattKeepalivePacketData.java
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -22,11 +22,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.OsConstants;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
diff --git a/core/java/android/net/util/IpUtils.java b/core/java/android/net/util/IpUtils.java
deleted file mode 100644
index e037c40..0000000
--- a/core/java/android/net/util/IpUtils.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.nio.BufferOverflowException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ShortBuffer;
-
-import static android.system.OsConstants.IPPROTO_TCP;
-import static android.system.OsConstants.IPPROTO_UDP;
-
-/**
- * @hide
- */
-public class IpUtils {
-    /**
-     * Converts a signed short value to an unsigned int value.  Needed
-     * because Java does not have unsigned types.
-     */
-    private static int intAbs(short v) {
-        return v & 0xFFFF;
-    }
-
-    /**
-     * Performs an IP checksum (used in IP header and across UDP
-     * payload) on the specified portion of a ByteBuffer.  The seed
-     * allows the checksum to commence with a specified value.
-     */
-    private static int checksum(ByteBuffer buf, int seed, int start, int end) {
-        int sum = seed;
-        final int bufPosition = buf.position();
-
-        // set position of original ByteBuffer, so that the ShortBuffer
-        // will be correctly initialized
-        buf.position(start);
-        ShortBuffer shortBuf = buf.asShortBuffer();
-
-        // re-set ByteBuffer position
-        buf.position(bufPosition);
-
-        final int numShorts = (end - start) / 2;
-        for (int i = 0; i < numShorts; i++) {
-            sum += intAbs(shortBuf.get(i));
-        }
-        start += numShorts * 2;
-
-        // see if a singleton byte remains
-        if (end != start) {
-            short b = buf.get(start);
-
-            // make it unsigned
-            if (b < 0) {
-                b += 256;
-            }
-
-            sum += b * 256;
-        }
-
-        sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
-        sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
-        int negated = ~sum;
-        return intAbs((short) negated);
-    }
-
-    private static int pseudoChecksumIPv4(
-            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
-        int partial = protocol + transportLen;
-        partial += intAbs(buf.getShort(headerOffset + 12));
-        partial += intAbs(buf.getShort(headerOffset + 14));
-        partial += intAbs(buf.getShort(headerOffset + 16));
-        partial += intAbs(buf.getShort(headerOffset + 18));
-        return partial;
-    }
-
-    private static int pseudoChecksumIPv6(
-            ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
-        int partial = protocol + transportLen;
-        for (int offset = 8; offset < 40; offset += 2) {
-            partial += intAbs(buf.getShort(headerOffset + offset));
-        }
-        return partial;
-    }
-
-    private static byte ipversion(ByteBuffer buf, int headerOffset) {
-        return (byte) ((buf.get(headerOffset) & (byte) 0xf0) >> 4);
-   }
-
-    public static short ipChecksum(ByteBuffer buf, int headerOffset) {
-        byte ihl = (byte) (buf.get(headerOffset) & 0x0f);
-        return (short) checksum(buf, 0, headerOffset, headerOffset + ihl * 4);
-    }
-
-    private static short transportChecksum(ByteBuffer buf, int protocol,
-            int ipOffset, int transportOffset, int transportLen) {
-        if (transportLen < 0) {
-            throw new IllegalArgumentException("Transport length < 0: " + transportLen);
-        }
-        int sum;
-        byte ver = ipversion(buf, ipOffset);
-        if (ver == 4) {
-            sum = pseudoChecksumIPv4(buf, ipOffset, protocol, transportLen);
-        } else if (ver == 6) {
-            sum = pseudoChecksumIPv6(buf, ipOffset, protocol, transportLen);
-        } else {
-            throw new UnsupportedOperationException("Checksum must be IPv4 or IPv6");
-        }
-
-        sum = checksum(buf, sum, transportOffset, transportOffset + transportLen);
-        if (protocol == IPPROTO_UDP && sum == 0) {
-            sum = (short) 0xffff;
-        }
-        return (short) sum;
-    }
-
-    public static short udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset) {
-        int transportLen = intAbs(buf.getShort(transportOffset + 4));
-        return transportChecksum(buf, IPPROTO_UDP, ipOffset, transportOffset, transportLen);
-    }
-
-    public static short tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset,
-            int transportLen) {
-        return transportChecksum(buf, IPPROTO_TCP, ipOffset, transportOffset, transportLen);
-    }
-
-    public static String addressAndPortToString(InetAddress address, int port) {
-        return String.format(
-                (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
-                address.getHostAddress(), port);
-    }
-
-    public static boolean isValidUdpOrTcpPort(int port) {
-        return port > 0 && port < 65536;
-    }
-}
diff --git a/core/java/android/permission/Permissions.md b/core/java/android/permission/Permissions.md
index d8ab618..4224b7a 100644
--- a/core/java/android/permission/Permissions.md
+++ b/core/java/android/permission/Permissions.md
@@ -203,7 +203,7 @@
 
 During development and testing a runtime permission can be granted via the `pm` shell command or by
 using the `UiAutomator.grantRuntimePermission` API call. Please note that this does _not_ grant the
-[app-op](#runtime-permissions-and-app-ops) synchronously. Unless the app needs to test the actual
+[app-op](#runtime-permissions-and-app_ops) synchronously. Unless the app needs to test the actual
 permission grant flow it is recommended to grant the runtime permissions during install using
 `adb install -g /my/package.apk`.
 
@@ -262,7 +262,7 @@
 silently fail.
 
 A secondary use case of the `AppOpsManager.noteOp` calls is to
-[track](../app/AppOps.md#Appops-for-tracking) which apps perform what runtime protected actions.
+[track](../app/AppOps.md#app_ops-for-tracking) which apps perform what runtime protected actions.
 
 #### Verifying an app has a runtime time permission
 
@@ -471,7 +471,7 @@
 
 ##### Location
 
-As described [above](#runtime-permissions-and-app-ops) the app-op mode for granted permissions is
+As described [above](#runtime-permissions-and-app_ops) the app-op mode for granted permissions is
 `MODE_ALLOWED` to allow access or `MODE_IGNORED` to suppress access.
 
 The important case is the case where the permission is granted and the app-op is `MODE_IGNORED`. In
@@ -848,7 +848,7 @@
 
 @Test
 fun onlySomeAppsAreAllowedToHavePermissionGranted() {
-    assertThat(whitelistedPkgs).containsAllIn(
+    assertThat(whitelistedPkgs).containsAtLeastElementsIn(
             context.packageManager.getInstalledPackages(MATCH_ALL)
                     .filter { pkg ->
                         context.checkPermission(android.Manifest.permission.MY_PRIVILEGED_PERMISSION, -1,
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index e1aa21e..8ac1d84e 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -2618,7 +2618,8 @@
             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
             intent.putExtra(ALARM_TIME, alarmTime);
             intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-            PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
+            PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
+                    PendingIntent.FLAG_IMMUTABLE);
             manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
         }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7df9a5f..8ad7669 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9088,6 +9088,7 @@
          * @see#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
                 "accessibility_magnification_mode";
 
@@ -9095,12 +9096,14 @@
          * Magnification mode value that magnifies whole display.
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN = 0x1;
 
         /**
          * Magnification mode value that magnifies magnify particular region in a window
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 0x2;
 
         /**
@@ -9108,6 +9111,7 @@
          * region in a window.
          * @hide
          */
+        @TestApi
         public static final int ACCESSIBILITY_MAGNIFICATION_MODE_ALL = 0x3;
 
         /**
@@ -9119,6 +9123,7 @@
          * @see#ACCESSIBILITY_MAGNIFICATION_MODE_ALL
          * @hide
          */
+        @TestApi
         public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
                 "accessibility_magnification_capability";
 
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 5d34c47..8f119fc 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -64,6 +64,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -1802,7 +1803,7 @@
          * {@link NotificationAssistantService}
          */
         public @NonNull List<Notification.Action> getSmartActions() {
-            return mSmartActions;
+            return mSmartActions == null ? Collections.emptyList() : mSmartActions;
         }
 
         /**
@@ -1810,7 +1811,7 @@
          * {@link NotificationAssistantService}
          */
         public @NonNull List<CharSequence> getSmartReplies() {
-            return mSmartReplies;
+            return mSmartReplies == null ? Collections.emptyList() : mSmartReplies;
         }
 
         /**
@@ -1864,8 +1865,9 @@
         }
 
         /**
-         * Returns whether this notification is a conversation notification.
-         * @hide
+         * Returns whether this notification is a conversation notification, and would appear
+         * in the conversation section of the notification shade, on devices that separate that
+         * type of notification.
          */
         public boolean isConversation() {
             return mIsConversation;
@@ -1880,7 +1882,10 @@
         }
 
         /**
-         * @hide
+         * Returns the shortcut information associated with this notification, if it is a
+         * {@link #isConversation() conversation notification}.
+         * <p>This might be null even if the notification is a conversation notification, if
+         * the posting app hasn't opted into the full conversation feature set yet.</p>
          */
         public @Nullable ShortcutInfo getShortcutInfo() {
             return mShortcutInfo;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c430a4d..4f05a59 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15165,6 +15165,42 @@
     }
 
     /**
+     * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+     * that the system has successfully initialized an {@link InputConnection} and it is ready for
+     * use.
+     *
+     * <p>The default implementation does nothing, since a view doesn't support input methods by
+     * default (see {@link #onCreateInputConnection}).
+     *
+     * @param inputConnection The {@link InputConnection} from {@link #onCreateInputConnection},
+     * after it's been fully initialized by the system.
+     * @param editorInfo The {@link EditorInfo} that was used to create the {@link InputConnection}.
+     * @param handler The dedicated {@link Handler} on which IPC method calls from input methods
+     * will be dispatched. This is the handler returned by {@link InputConnection#getHandler()}. If
+     * that method returns null, this parameter will be null also.
+     *
+     * @hide
+     */
+    public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+            @NonNull EditorInfo editorInfo, @Nullable Handler handler) {}
+
+    /**
+     * Called by the {@link android.view.inputmethod.InputMethodManager} to notify the application
+     * that the {@link InputConnection} has been closed.
+     *
+     * <p>The default implementation does nothing, since a view doesn't support input methods by
+     * default (see {@link #onCreateInputConnection}).
+     *
+     * <p><strong>Note:</strong> This callback is not invoked if the view is already detached when
+     * the {@link InputConnection} is closed or the connection is not valid and managed by
+     * {@link com.android.server.inputmethod.InputMethodManagerService}.
+     * TODO(b/170645312): Before un-hiding this API, handle the detached view scenario.
+     *
+     * @hide
+     */
+    public void onInputConnectionClosedInternal() {}
+
+    /**
      * Called by the {@link android.view.inputmethod.InputMethodManager}
      * when a view who is not the current
      * input connection target is trying to make a call on the manager.  The
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b8f04159..5785999 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1006,6 +1006,24 @@
                 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
@@ -1940,6 +1958,8 @@
         InputConnection ic = view.onCreateInputConnection(tba);
         if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
 
+        final Handler icHandler;
+        InputBindResult res = null;
         synchronized (mH) {
             // Now that we are locked again, validate that our state hasn't
             // changed.
@@ -1976,7 +1996,6 @@
                 mCursorCandEnd = -1;
                 mCursorRect.setEmpty();
                 mCursorAnchorInfo = null;
-                final Handler icHandler;
                 missingMethodFlags = InputConnectionInspector.getMissingMethodFlags(ic);
                 if ((missingMethodFlags & InputConnectionInspector.MissingMethodFlags.GET_HANDLER)
                         != 0) {
@@ -1990,6 +2009,7 @@
             } else {
                 servedContext = null;
                 missingMethodFlags = 0;
+                icHandler = null;
             }
             mServedInputConnectionWrapper = servedContext;
 
@@ -1997,7 +2017,7 @@
                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
                         + ic + " tba=" + tba + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
-                final InputBindResult res = mService.startInputOrWindowGainedFocus(
+                res = mService.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
                         softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
                         view.getContext().getApplicationInfo().targetSdkVersion);
@@ -2036,6 +2056,15 @@
             }
         }
 
+        // Notify the app that the InputConnection is initialized and ready for use.
+        if (ic != null && res != null && res.method != null) {
+            if (DEBUG) {
+                Log.v(TAG, "Calling View.onInputConnectionOpened: view= " + view
+                        + ", ic=" + ic + ", tba=" + tba + ", handler=" + icHandler);
+            }
+            view.onInputConnectionOpenedInternal(ic, tba, icHandler);
+        }
+
         return true;
     }
 
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 2e5ee04..5bcfa8b 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -34,7 +34,7 @@
     private final UserInfo mUserInfo;
     private final PackageInfo mPackageInfo;
 
-    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.S;
 
     public UserPackage(UserInfo user, PackageInfo packageInfo) {
         this.mUserInfo = user;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 8790bbd..5fc9344 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -47,7 +47,7 @@
     // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
     /** @hide */
     private static final String CHROMIUM_WEBVIEW_FACTORY =
-            "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForS";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index dd4409c..3624f0d 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -16,7 +16,18 @@
 
 package com.android.internal.jank;
 
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -63,18 +74,19 @@
     // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
     @VisibleForTesting
     public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
+            // This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
             UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
-            NO_STATSD_LOGGING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_EXPAND_COLLAPSE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_QS_SCROLL_SWIPE,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP,
+            UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH,
     };
 
     private static volatile InteractionJankMonitor sInstance;
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index 4f687f1..e6a9623 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -45,6 +45,7 @@
     private static final String TAG = "KernelSingleProcCpuThreadRdr";
 
     private static final boolean DEBUG = false;
+    private static final boolean NATIVE_ENABLED = true;
 
     /**
      * The name of the file to read CPU statistics from, must be found in {@code
@@ -64,7 +65,7 @@
     private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
 
     /** See https://man7.org/linux/man-pages/man5/proc.5.html */
-    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[]{
+    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
             PROC_SPACE_TERM,
             PROC_SPACE_TERM,
             PROC_SPACE_TERM,
@@ -162,6 +163,7 @@
     /**
      * Get the total and per-thread CPU usage of the process with the PID specified in the
      * constructor.
+     *
      * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
      *                          be aggregated as a group.  This is expected to be a subset
      *                          of all thread IDs owned by the process.
@@ -173,6 +175,20 @@
                     + mPid);
         }
 
+        int cpuFrequencyCount = getCpuFrequencyCount();
+        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+
+        if (NATIVE_ENABLED) {
+            boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
+                    selectedThreadIds, processCpuUsage.processCpuTimesMillis,
+                    processCpuUsage.threadCpuTimesMillis,
+                    processCpuUsage.selectedThreadCpuTimesMillis);
+            if (!result) {
+                return null;
+            }
+            return processCpuUsage;
+        }
+
         if (!isSorted(selectedThreadIds)) {
             throw new IllegalArgumentException("selectedThreadIds is not sorted: "
                     + Arrays.toString(selectedThreadIds));
@@ -189,8 +205,6 @@
 
         long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
 
-        int cpuFrequencyCount = getCpuFrequencyCount();
-        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
         try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
             for (Path threadDirectory : threadPaths) {
                 readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
@@ -274,4 +288,8 @@
         }
         return true;
     }
+
+    private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
+            long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
+            long[] selectedThreadCpuTimesMillis);
 }
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index a23fc4b..0bafb2f 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -279,6 +279,7 @@
             final Runnable mScreenshotTimeout = () -> {
                 synchronized (mScreenshotLock) {
                     if (mScreenshotConnection != null) {
+                        Log.e(TAG, "Timed out before getting screenshot capture response");
                         mContext.unbindService(mScreenshotConnection);
                         mScreenshotConnection = null;
                         mScreenshotService = null;
@@ -353,6 +354,7 @@
                                 mScreenshotService = null;
                                 // only log an error if we're still within the timeout period
                                 if (handler.hasCallbacks(mScreenshotTimeout)) {
+                                    Log.e(TAG, "Screenshot service disconnected");
                                     handler.removeCallbacks(mScreenshotTimeout);
                                     notifyScreenshotError();
                                 }
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
new file mode 100644
index 0000000..461e116
--- /dev/null
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -0,0 +1,202 @@
+/*
+ * 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.internal.view;
+
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ * ScrollCapture for RecyclerView and <i>RecyclerView-like</i> ViewGroups.
+ * <p>
+ * Requirements for proper operation:
+ * <ul>
+ * <li>at least one visible child view</li>
+ * <li>scrolls by pixels in response to {@link View#scrollBy(int, int)}.
+ * <li>reports ability to scroll with {@link View#canScrollVertically(int)}
+ * <li>properly implements {@link ViewParent#requestChildRectangleOnScreen(View, Rect, boolean)}
+ * </ul>
+ *
+ * @see ScrollCaptureViewSupport
+ */
+public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
+
+    // Experiment
+    private static final boolean DISABLE_ANIMATORS = false;
+    // Experiment
+    private static final boolean STOP_RENDER_THREAD = false;
+
+    private static final String TAG = "RVCaptureHelper";
+    private int mScrollDelta;
+    private boolean mScrollBarWasEnabled;
+    private int mOverScrollMode;
+    private float mDurationScale;
+
+    @Override
+    public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
+        mScrollDelta = 0;
+
+        mOverScrollMode = view.getOverScrollMode();
+        view.setOverScrollMode(View.OVER_SCROLL_NEVER);
+
+        mScrollBarWasEnabled = view.isVerticalScrollBarEnabled();
+        view.setVerticalScrollBarEnabled(false);
+        if (DISABLE_ANIMATORS) {
+            mDurationScale = ValueAnimator.getDurationScale();
+            ValueAnimator.setDurationScale(0);
+        }
+        if (STOP_RENDER_THREAD) {
+            view.getThreadedRenderer().stop();
+        }
+    }
+
+    @Override
+    public ScrollResult onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
+            Rect requestRect) {
+        ScrollResult result = new ScrollResult();
+        result.requestedArea = new Rect(requestRect);
+        result.scrollDelta = mScrollDelta;
+        result.availableArea = new Rect(); // empty
+
+        Log.d(TAG, "current scrollDelta: " + mScrollDelta);
+        if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
+            Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
+            return result; // result.availableArea == empty Rect
+        }
+
+        // move from scrollBounds-relative to parent-local coordinates
+        Rect requestedContainerBounds = new Rect(requestRect);
+        requestedContainerBounds.offset(0, -mScrollDelta);
+        requestedContainerBounds.offset(scrollBounds.left, scrollBounds.top);
+
+        // requestedContainerBounds is now in recyclerview-local coordinates
+        Log.d(TAG, "requestedContainerBounds: " + requestedContainerBounds);
+
+        // Save a copy for later
+        View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
+        if (anchor == null) {
+            Log.d(TAG, "Failed to locate anchor view");
+            return result; // result.availableArea == null
+        }
+
+        Log.d(TAG, "Anchor view:" + anchor);
+        Rect requestedContentBounds = new Rect(requestedContainerBounds);
+        recyclerView.offsetRectIntoDescendantCoords(anchor, requestedContentBounds);
+
+        Log.d(TAG, "requestedContentBounds = " + requestedContentBounds);
+        int prevAnchorTop = anchor.getTop();
+        // Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
+        Rect input = new Rect(requestedContentBounds);
+        if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
+            int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
+            Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
+            mScrollDelta += scrolled; // view.top-- is equivalent to parent.scrollY++
+            result.scrollDelta = mScrollDelta;
+            Log.d(TAG, "requestedContentBounds, (post-request-rect) = " + requestedContentBounds);
+        }
+
+        requestedContainerBounds.set(requestedContentBounds);
+        recyclerView.offsetDescendantRectToMyCoords(anchor, requestedContainerBounds);
+        Log.d(TAG, "requestedContainerBounds, (post-scroll): " + requestedContainerBounds);
+
+        Rect recyclerLocalVisible = new Rect(scrollBounds);
+        recyclerView.getLocalVisibleRect(recyclerLocalVisible);
+        Log.d(TAG, "recyclerLocalVisible: " + recyclerLocalVisible);
+
+        if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
+            // Requested area is still not visible
+            Log.d(TAG, "requested bounds not visible!");
+            return result;
+        }
+        Rect available = new Rect(requestedContainerBounds);
+        available.offset(-scrollBounds.left, -scrollBounds.top);
+        available.offset(0, mScrollDelta);
+        result.availableArea = available;
+        Log.d(TAG, "availableArea: " + result.availableArea);
+        return result;
+    }
+
+    /**
+     * Find a view that is located "closest" to targetRect. Returns the first view to fully
+     * vertically overlap the target targetRect. If none found, returns the view with an edge
+     * nearest the target targetRect.
+     *
+     * @param parent the parent vertical layout
+     * @param targetRect a rectangle in local coordinates of <code>parent</code>
+     * @return a child view within parent matching the criteria or null
+     */
+    static View findChildNearestTarget(ViewGroup parent, Rect targetRect) {
+        View selected = null;
+        int minCenterDistance = Integer.MAX_VALUE;
+        int maxOverlap = 0;
+
+        // allowable center-center distance, relative to targetRect.
+        // if within this range, taller views are preferred
+        final float preferredRangeFromCenterPercent = 0.25f;
+        final int preferredDistance =
+                (int) (preferredRangeFromCenterPercent * targetRect.height());
+
+        Rect parentLocalVis = new Rect();
+        parent.getLocalVisibleRect(parentLocalVis);
+        Log.d(TAG, "findChildNearestTarget: parentVis=" + parentLocalVis
+                + " targetRect=" + targetRect);
+
+        Rect frame = new Rect();
+        for (int i = 0; i < parent.getChildCount(); i++) {
+            final View child = parent.getChildAt(i);
+            child.getHitRect(frame);
+            Log.d(TAG, "child #" + i + " hitRect=" + frame);
+
+            if (child.getVisibility() != View.VISIBLE) {
+                Log.d(TAG, "child #" + i + " is not visible");
+                continue;
+            }
+
+            int centerDistance = Math.abs(targetRect.centerY() - frame.centerY());
+            Log.d(TAG, "child #" + i + " : center to center: " + centerDistance + "px");
+
+            if (centerDistance < minCenterDistance) {
+                // closer to center
+                minCenterDistance = centerDistance;
+                selected = child;
+            } else if (frame.intersect(targetRect) && (frame.height() > preferredDistance)) {
+                // within X% pixels of center, but taller
+                selected = child;
+            }
+        }
+        return selected;
+    }
+
+
+    @Override
+    public void onPrepareForEnd(@NonNull ViewGroup view) {
+        // Restore original position and state
+        view.scrollBy(0, mScrollDelta);
+        view.setOverScrollMode(mOverScrollMode);
+        view.setVerticalScrollBarEnabled(mScrollBarWasEnabled);
+        if (DISABLE_ANIMATORS) {
+            ValueAnimator.setDurationScale(mDurationScale);
+        }
+        if (STOP_RENDER_THREAD) {
+            view.getThreadedRenderer().start();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index c589afde..ae1a815 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -17,8 +17,11 @@
 package com.android.internal.view;
 
 import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.util.Log;
 import android.view.ScrollCaptureCallback;
 import android.view.View;
 import android.view.ViewGroup;
@@ -29,6 +32,12 @@
 public class ScrollCaptureInternal {
     private static final String TAG = "ScrollCaptureInternal";
 
+    // Log found scrolling views
+    private static final boolean DEBUG = true;
+
+    // Log all investigated views, as well as heuristic checks
+    private static final boolean DEBUG_VERBOSE = false;
+
     private static final int UP = -1;
     private static final int DOWN = 1;
 
@@ -57,38 +66,72 @@
      * This needs to be fast and not alloc memory. It's called on everything in the tree not marked
      * as excluded during scroll capture search.
      */
-    public static int detectScrollingType(View view) {
+    private static int detectScrollingType(View view) {
         // Must be a ViewGroup
         if (!(view instanceof ViewGroup)) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: not a subclass of ViewGroup");
+            }
             return TYPE_FIXED;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: is a subclass of ViewGroup");
+        }
         // Confirm that it can scroll.
         if (!(view.canScrollVertically(DOWN) || view.canScrollVertically(UP))) {
             // Nothing to scroll here, move along.
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: cannot be scrolled");
+            }
             return TYPE_FIXED;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: can be scrolled up or down");
+        }
         // ScrollViews accept only a single child.
         if (((ViewGroup) view).getChildCount() > 1) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollable with multiple children");
+            }
             return TYPE_RECYCLING;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: less than two child views");
+        }
         //Because recycling containers don't use scrollY, a non-zero value means Scroll view.
         if (view.getScrollY() != 0) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollY != 0");
+            }
             return TYPE_SCROLLING;
         }
+        Log.v(TAG, "hint: scrollY == 0");
         // Since scrollY cannot be negative, this means a Recycling view.
         if (view.canScrollVertically(UP)) {
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: able to scroll up");
+            }
             return TYPE_RECYCLING;
         }
-        // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: cannot be scrolled up");
+        }
 
+        // canScrollVertically(UP) == false, getScrollY() == 0, getChildCount() == 1.
         // For Recycling containers, this should be a no-op (RecyclerView logs a warning)
         view.scrollTo(view.getScrollX(), 1);
 
         // A scrolling container would have moved by 1px.
         if (view.getScrollY() == 1) {
             view.scrollTo(view.getScrollX(), 0);
+            if (DEBUG_VERBOSE) {
+                Log.v(TAG, "hint: scrollTo caused scrollY to change");
+            }
             return TYPE_SCROLLING;
         }
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "hint: scrollTo did not cause scrollY to change");
+        }
         return TYPE_RECYCLING;
     }
 
@@ -99,19 +142,61 @@
      * @param localVisibleRect the visible area of the given view in local coordinates, as supplied
      *                         by the view parent
      * @param positionInWindow the offset of localVisibleRect within the window
-     *
      * @return a new callback or null if the View isn't supported
      */
     @Nullable
     public ScrollCaptureCallback requestCallback(View view, Rect localVisibleRect,
             Point positionInWindow) {
         // Nothing to see here yet.
+        if (DEBUG_VERBOSE) {
+            Log.v(TAG, "scroll capture: checking " + view.getClass().getName()
+                    + "[" + resolveId(view.getContext(), view.getId()) + "]");
+        }
         int i = detectScrollingType(view);
         switch (i) {
             case TYPE_SCROLLING:
+                if (DEBUG) {
+                    Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+                            + "[" + resolveId(view.getContext(), view.getId()) + "]"
+                            + " -> TYPE_SCROLLING");
+                }
                 return new ScrollCaptureViewSupport<>((ViewGroup) view,
                         new ScrollViewCaptureHelper());
+            case TYPE_RECYCLING:
+                if (DEBUG) {
+                    Log.d(TAG, "scroll capture: FOUND " + view.getClass().getName()
+                            + "[" + resolveId(view.getContext(), view.getId()) + "]"
+                            + " -> TYPE_RECYCLING");
+                }
+                return new ScrollCaptureViewSupport<>((ViewGroup) view,
+                        new RecyclerViewCaptureHelper());
+            case TYPE_FIXED:
+                // ignore
+                break;
+
         }
         return null;
     }
+
+    // Lifted from ViewDebug (package protected)
+
+    private static String formatIntToHexString(int value) {
+        return "0x" + Integer.toHexString(value).toUpperCase();
+    }
+
+    static String resolveId(Context context, int id) {
+        String fieldValue;
+        final Resources resources = context.getResources();
+        if (id >= 0) {
+            try {
+                fieldValue = resources.getResourceTypeName(id) + '/'
+                        + resources.getResourceEntryName(id);
+            } catch (Resources.NotFoundException e) {
+                fieldValue = "id/" + formatIntToHexString(id);
+            }
+        } else {
+            fieldValue = "NO_ID";
+        }
+        return fieldValue;
+    }
 }
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
index a92e978..9829d0b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java
@@ -17,7 +17,6 @@
 package com.android.internal.view;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.view.View;
 
@@ -62,8 +61,8 @@
      * @param view the view being captured
      * @return true if the callback should respond to a request with scroll bounds
      */
-    default boolean onAcceptSession(@Nullable V view) {
-        return view != null && view.isVisibleToUser()
+    default boolean onAcceptSession(@NonNull V view) {
+        return view.isVisibleToUser()
                 && (view.canScrollVertically(UP) || view.canScrollVertically(DOWN));
     }
 
@@ -73,7 +72,7 @@
      *
      * @param view the view being captured
      */
-    default Rect onComputeScrollBounds(@Nullable V view) {
+    @NonNull default Rect onComputeScrollBounds(@NonNull V view) {
         return new Rect(view.getPaddingLeft(), view.getPaddingTop(),
                 view.getWidth() - view.getPaddingRight(),
                 view.getHeight() - view.getPaddingBottom());
@@ -88,7 +87,7 @@
      * @param view         the view being captured
      * @param scrollBounds the bounds within {@code view} where content scrolls
      */
-    void onPrepareForStart(@NonNull V view, Rect scrollBounds);
+    void onPrepareForStart(@NonNull V view, @NonNull Rect scrollBounds);
 
     /**
      * Map the request onto the screen.
@@ -105,7 +104,9 @@
      *                     content to capture for the request
      * @return the result of the request as a {@link ScrollResult}
      */
-    ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect);
+    @NonNull
+    ScrollResult onScrollRequested(@NonNull V view, @NonNull Rect scrollBounds,
+            @NonNull Rect requestRect);
 
     /**
      * Restore the target after capture.
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 7b4f73f..85fa791 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -23,8 +23,8 @@
 import android.graphics.RectF;
 import android.graphics.RenderNode;
 import android.os.Handler;
-import android.os.SystemClock;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.view.ScrollCaptureCallback;
 import android.view.ScrollCaptureSession;
 import android.view.Surface;
@@ -46,8 +46,12 @@
  */
 public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
 
+    public static final long NO_FRAME_PRODUCED = -1;
+
     private static final String TAG = "ScrollCaptureViewSupport";
 
+    private static final boolean WAIT_FOR_ANIMATION = true;
+
     private final WeakReference<V> mWeakView;
     private final ScrollCaptureViewHelper<V> mViewHelper;
     private ViewRenderer mRenderer;
@@ -99,12 +103,16 @@
         V view = mWeakView.get();
         if (view == null || !view.isVisibleToUser()) {
             // Signal to the controller that we have a problem and can't continue.
-            session.notifyBufferSent(0, null);
+            session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect());
             return;
         }
         // Ask the view to scroll as needed to bring this area into view.
         ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
                 requestRect);
+        if (scrollResult.availableArea.isEmpty()) {
+            session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea);
+            return;
+        }
         view.invalidate(); // don't wait for vsync
 
         // For image capture, shift back by scrollDelta to arrive at the location within the view
@@ -112,8 +120,19 @@
         Rect viewCaptureArea = new Rect(scrollResult.availableArea);
         viewCaptureArea.offset(0, -scrollResult.scrollDelta);
 
-        mRenderer.renderView(view, viewCaptureArea, mUiHandler,
-                (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea));
+        if (WAIT_FOR_ANIMATION) {
+            Log.d(TAG, "render: delaying until animation");
+            view.postOnAnimation(() ->  {
+                Log.d(TAG, "postOnAnimation(): rendering now");
+                long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+                Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea);
+
+                session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+            });
+        } else {
+            long resultFrame = mRenderer.renderView(view, viewCaptureArea);
+            session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+        }
     }
 
     @Override
@@ -132,8 +151,7 @@
 
     /**
      * Internal helper class which assists in rendering sections of the view hierarchy relative to a
-     * given view. Used by framework implementations of ScrollCaptureHandler to render and dispatch
-     * image requests.
+     * given view.
      */
     static final class ViewRenderer {
         // alpha, "reasonable default" from Javadoc
@@ -157,14 +175,11 @@
         private final Matrix mTempMatrix = new Matrix();
         private final int[] mTempLocation = new int[2];
         private long mLastRenderedSourceDrawingId = -1;
-
-
-        public interface FrameCompleteListener {
-            void onFrameComplete(long frameNumber);
-        }
+        private Surface mSurface;
 
         ViewRenderer() {
             mRenderer = new HardwareRenderer();
+            mRenderer.setName("ScrollCapture");
             mCaptureRenderNode = new RenderNode("ScrollCaptureRoot");
             mRenderer.setContentRoot(mCaptureRenderNode);
 
@@ -173,6 +188,7 @@
         }
 
         public void setSurface(Surface surface) {
+            mSurface = surface;
             mRenderer.setSurface(surface);
         }
 
@@ -223,20 +239,38 @@
             mCaptureRenderNode.endRecording();
         }
 
-        public void renderView(View view, Rect sourceRect, Handler handler,
-                FrameCompleteListener frameListener) {
+        public long renderView(View view, Rect sourceRect) {
             if (updateForView(view)) {
                 setupLighting(view);
             }
             view.invalidate();
             updateRootNode(view, sourceRect);
             HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest();
-            request.setVsyncTime(SystemClock.elapsedRealtimeNanos());
-            // private API b/c request.setFrameCommitCallback does not provide access to frameNumber
-            mRenderer.setFrameCompleteCallback(
-                    frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr)));
+            long timestamp = System.nanoTime();
+            request.setVsyncTime(timestamp);
+
+            // Would be nice to access nextFrameNumber from HwR without having to hold on to Surface
+            final long frameNumber = mSurface.getNextFrameNumber();
+
+            // Block until a frame is presented to the Surface
             request.setWaitForPresent(true);
-            request.syncAndDraw();
+
+            switch (request.syncAndDraw()) {
+                case HardwareRenderer.SYNC_OK:
+                case HardwareRenderer.SYNC_REDRAW_REQUESTED:
+                    return frameNumber;
+
+                case HardwareRenderer.SYNC_FRAME_DROPPED:
+                    Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
+                    break;
+                case HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND:
+                    Log.e(TAG, "syncAndDraw(): SYNC_LOST_SURFACE !");
+                    break;
+                case HardwareRenderer.SYNC_CONTEXT_IS_STOPPED:
+                    Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
+                    break;
+            }
+            return NO_FRAME_PRODUCED;
         }
 
         public void trimMemory() {
@@ -244,6 +278,7 @@
         }
 
         public void destroy() {
+            mSurface = null;
             mRenderer.destroy();
         }
 
@@ -254,6 +289,5 @@
             mTempMatrix.mapRect(mTempRectF);
             mTempRectF.round(outRect);
         }
-
     }
 }
diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
index 1514b9a..a1d202e 100644
--- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java
@@ -57,10 +57,6 @@
 
     public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds,
             Rect requestRect) {
-        final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
-        if (contentView == null) {
-            return null;
-        }
         /*
                +---------+ <----+ Content [25,25 - 275,1025] (w=250,h=1000)
                |         |
@@ -88,9 +84,6 @@
             \__ Requested Bounds[0,300 - 200,400] (200x100)
        */
 
-        ScrollResult result = new ScrollResult();
-        result.requestedArea = new Rect(requestRect);
-
         // 0) adjust the requestRect to account for scroll change since start
         //
         //  Scroll Bounds[50,50 - 250,250]  (w=200,h=200)
@@ -99,6 +92,17 @@
         // (y-100) (scrollY - mStartScrollY)
         int scrollDelta = view.getScrollY() - mStartScrollY;
 
+        final ScrollResult result = new ScrollResult();
+        result.requestedArea = new Rect(requestRect);
+        result.scrollDelta = scrollDelta;
+        result.availableArea = new Rect();
+
+        final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE
+        if (contentView == null) {
+            // No child view? Cannot continue.
+            return result;
+        }
+
         //  1) Translate request rect to make it relative to container view
         //
         //  Container View [0,0 - 300,300] (scrollY=200)
@@ -133,7 +137,7 @@
         // TODO: crop capture area to avoid occlusions/minimize scroll changes
 
         Point offset = new Point();
-        final Rect available = new Rect(requestedContentBounds); // empty
+        final Rect available = new Rect(requestedContentBounds);
         if (!view.getChildVisibleRect(contentView, available, offset)) {
             available.setEmpty();
             result.availableArea = available;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index adb0fad..4f97975 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -184,6 +184,7 @@
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
                 "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+                "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                 "com_android_internal_os_KernelSingleUidTimeReader.cpp",
                 "com_android_internal_os_Zygote.cpp",
                 "com_android_internal_os_ZygoteInit.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5b1196d..27b23bd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -189,6 +189,7 @@
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
 extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1581,6 +1582,7 @@
         REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
         REG_JNI(register_com_android_internal_os_FuseAppLoop),
         REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+        REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),
         REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
 };
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 5c4c509..1ca45fe 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -338,7 +338,7 @@
             return (jint)AUDIO_JAVA_BAD_VALUE;
         }
         const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL);
-        AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
+        AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address);
         audioDeviceTypeAddrVector.push_back(dev);
         env->ReleaseStringUTFChars((jstring)addrJobj, address);
     }
@@ -820,7 +820,8 @@
                                                bool useInMask)
 {
     nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex);
-    nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
+    nAudioGainConfig->mode =
+            (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode);
     ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index);
     jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask);
     audio_channel_mask_t nMask;
@@ -940,8 +941,8 @@
 
     jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig,
             gAudioPortConfigFields.mPort);
-    nAudioPortConfig->ext.device.type = env->GetIntField(jAudioDevicePort,
-            gAudioPortFields.mType);
+    nAudioPortConfig->ext.device.type =
+            (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType);
     jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort,
             gAudioPortFields.mAddress);
     const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
@@ -2334,7 +2335,7 @@
 
 static jint
 android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jint uid, jint flags) {
-    return AudioSystem::setAllowedCapturePolicy(uid, flags);
+    return AudioSystem::setAllowedCapturePolicy(uid, static_cast<audio_flags_mask_t>(flags));
 }
 
 static jint
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
new file mode 100644
index 0000000..52bed6b
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+#include <dirent.h>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android_runtime/Log.h>
+
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+namespace android {
+
+// Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
+static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
+
+// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
+// file names in the /proc/<pid>/task directory.
+static bool getThreadIds(const std::string &procPath, const pid_t pid,
+                         std::vector<pid_t> &outThreadIds) {
+    std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+
+    struct dirent **dirlist;
+    int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
+    if (threadCount == -1) {
+        ALOGE("Cannot read directory %s", taskPath.c_str());
+        return false;
+    }
+
+    outThreadIds.reserve(threadCount);
+
+    for (int i = 0; i < threadCount; i++) {
+        pid_t tid;
+        if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
+            outThreadIds.push_back(tid);
+        }
+        free(dirlist[i]);
+    }
+    free(dirlist);
+
+    return true;
+}
+
+// Reads contents of a time_in_state file and returns times as a vector of times per frequency
+// A time_in_state file contains pairs of frequency - time (in jiffies):
+//
+//    cpu0
+//    300000 30
+//    403200 0
+//    cpu4
+//    710400 10
+//    825600 20
+//    940800 30
+//
+static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
+                                 const size_t frequencyCount,
+                                 std::vector<uint64_t> &outThreadTimeInState) {
+    std::string timeInStateFilePath =
+            android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
+    std::string data;
+
+    if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
+        ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
+        return false;
+    }
+
+    auto lines = android::base::Split(data, "\n");
+    size_t index = 0;
+    for (const auto &line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        auto numbers = android::base::Split(line, " ");
+        if (numbers.size() != 2) {
+            continue;
+        }
+        uint64_t timeInState;
+        if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
+            ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+            return false;
+        }
+        if (index < frequencyCount) {
+            outThreadTimeInState[index] = timeInState;
+        }
+        index++;
+    }
+
+    if (index != frequencyCount) {
+        ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
+              (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
+              (uint32_t)frequencyCount);
+        return false;
+    }
+
+    return true;
+}
+
+static int pidCompare(const void *a, const void *b) {
+    return (*(pid_t *)a - *(pid_t *)b);
+}
+
+static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
+                                    const size_t selectedThreadCount) {
+    return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
+}
+
+// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// time in state data for all threads.  Also, separately aggregates time in state for
+// selected threads whose TIDs are passes as selectedThreadIds.
+static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
+                                    const std::vector<pid_t> &threadIds,
+                                    const size_t frequencyCount, const pid_t *selectedThreadIds,
+                                    const size_t selectedThreadCount,
+                                    uint64_t *threadCpuTimesMillis,
+                                    uint64_t *selectedThreadCpuTimesMillis) {
+    for (size_t j = 0; j < frequencyCount; j++) {
+        threadCpuTimesMillis[j] = 0;
+        selectedThreadCpuTimesMillis[j] = 0;
+    }
+
+    for (size_t i = 0; i < threadIds.size(); i++) {
+        pid_t tid = threadIds[i];
+        std::vector<uint64_t> timeInState(frequencyCount);
+        if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
+            continue;
+        }
+
+        bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
+        for (size_t j = 0; j < frequencyCount; j++) {
+            threadCpuTimesMillis[j] += timeInState[j];
+            if (selectedThread) {
+                selectedThreadCpuTimesMillis[j] += timeInState[j];
+            }
+        }
+    }
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] *= gJiffyMillis;
+        selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
+    }
+}
+
+// Reads process utime and stime from the /proc/<pid>/stat file.
+// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
+static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
+                              uint64_t &outTimeMillis) {
+    std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
+    std::string data;
+    if (!android::base::ReadFileToString(statFilePath, &data)) {
+        return false;
+    }
+
+    auto fields = android::base::Split(data, " ");
+    uint64_t utime, stime;
+
+    // Field 14 (counting from 1) is utime - process time in user space, in jiffies
+    // Field 15 (counting from 1) is stime - process time in system space, in jiffies
+    if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
+        !android::base::ParseUint(fields[14], &stime)) {
+        ALOGE("Invalid file format %s", statFilePath.c_str());
+        return false;
+    }
+
+    outTimeMillis = (utime + stime) * gJiffyMillis;
+    return true;
+}
+
+// Estimates per cluster per frequency CPU time for the entire process
+// by distributing the total process CPU time proportionately to how much
+// CPU time its threads took on those clusters/frequencies.  This algorithm
+// works more accurately when when we have equally distributed concurrency.
+// TODO(b/169279846): obtain actual process CPU times from the kernel
+static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
+                                       const uint64_t *threadCpuTimesMillis,
+                                       const size_t frequencyCount,
+                                       uint64_t *processCpuTimesMillis) {
+    uint64_t totalCpuTimeAllThreads = 0;
+    for (size_t i = 0; i < frequencyCount; i++) {
+        totalCpuTimeAllThreads += threadCpuTimesMillis[i];
+    }
+
+    if (totalCpuTimeAllThreads != 0) {
+        for (size_t i = 0; i < frequencyCount; i++) {
+            processCpuTimesMillis[i] =
+                    processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
+        }
+    } else {
+        for (size_t i = 0; i < frequencyCount; i++) {
+            processCpuTimesMillis[i] = 0;
+        }
+    }
+}
+
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
+                                    jintArray selectedThreadIdArray,
+                                    jlongArray processCpuTimesMillisArray,
+                                    jlongArray threadCpuTimesMillisArray,
+                                    jlongArray selectedThreadCpuTimesMillisArray) {
+    ScopedUtfChars procPathChars(env, procPath);
+    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+    ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+    ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
+    ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+
+    std::string procPathStr(procPathChars.c_str());
+
+    // Get all thread IDs for the process.
+    std::vector<pid_t> threadIds;
+    if (!getThreadIds(procPathStr, pid, threadIds)) {
+        ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
+        return false;
+    }
+
+    size_t frequencyCount = processCpuTimesMillis.size();
+
+    if (threadCpuTimesMillis.size() != frequencyCount) {
+        ALOGE("Invalid array length: threadCpuTimesMillis");
+        return false;
+    }
+    if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
+        ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+        return false;
+    }
+
+    aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
+                            selectedThreadIds.size(),
+                            reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+                            reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
+
+    uint64_t processCpuTime;
+    bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
+    if (ret) {
+        estimateProcessTimeInState(processCpuTime,
+                                   reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
+                                   frequencyCount,
+                                   reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+    }
+    return ret;
+}
+
+static const JNINativeMethod g_single_methods[] = {
+        {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+};
+
+int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
+    return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleProcessCpuThreadReader",
+                                g_single_methods, NELEM(g_single_methods));
+}
+
+} // namespace android
diff --git a/core/proto/android/server/vibratorservice.proto b/core/proto/android/server/vibratorservice.proto
index 281a25e..9e42e9e 100644
--- a/core/proto/android/server/vibratorservice.proto
+++ b/core/proto/android/server/vibratorservice.proto
@@ -21,6 +21,12 @@
 
 import "frameworks/base/core/proto/android/privacy.proto";
 
+message OneShotProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    repeated int32 duration = 1;
+    repeated int32 amplitude = 2;
+}
+
 message WaveformProto {
    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
    repeated int32 timings = 1;
@@ -35,20 +41,41 @@
     optional int32 fallback = 3;
 }
 
+message ComposedProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    repeated int32 effect_ids = 1;
+    repeated float effect_scales = 2;
+    repeated int32 delays = 3;
+}
+
 // A com.android.os.VibrationEffect object.
 message VibrationEffectProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    optional OneShotProto oneshot = 3;
     optional WaveformProto waveform = 1;
     optional PrebakedProto prebaked = 2;
+    optional ComposedProto composed = 4;
 }
 
+message VibrationAttributesProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    optional int32 usage = 1;
+    optional int32 audio_usage = 2;
+    optional int32 flags = 3;
+}
+
+// Next id: 7
 message VibrationProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     optional int64 start_time = 1;
+    optional int64 end_time = 4;
     optional VibrationEffectProto effect = 2;
-    optional VibrationEffectProto origin_effect = 3;
+    optional VibrationEffectProto original_effect = 3;
+    optional VibrationAttributesProto attributes = 5;
+    optional int32 status = 6;
 }
 
+// Next id: 17
 message VibratorServiceDumpProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     optional VibrationProto current_vibration = 1;
@@ -57,10 +84,14 @@
     optional bool vibrator_under_external_control = 4;
     optional bool low_power_mode = 5;
     optional int32 haptic_feedback_intensity = 6;
+    optional int32 haptic_feedback_default_intensity = 14;
     optional int32 notification_intensity = 7;
+    optional int32 notification_default_intensity = 15;
     optional int32 ring_intensity = 8;
+    optional int32 ring_default_intensity = 16;
     repeated VibrationProto previous_ring_vibrations = 9;
     repeated VibrationProto previous_notification_vibrations = 10;
     repeated VibrationProto previous_alarm_vibrations = 11;
     repeated VibrationProto previous_vibrations = 12;
+    repeated VibrationProto previous_external_vibrations = 13;
 }
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ed13069..e2f4f2f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3488,6 +3488,14 @@
     <permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
+         to ensure that only the system can bind to it.
+         @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_MUSIC_RECOGNITION_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a android.service.autofill.augmented.AugmentedAutofillService,
          to ensure that only the system can bind to it.
          @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -4533,6 +4541,12 @@
     <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
         android:protectionLevel="signature" />
 
+    <!-- Allows access to TestApis for various components in the biometric stack, including
+         FingerprintService, FaceService, BiometricService. Used by com.android.server.biometrics
+         CTS tests. @hide @TestApi -->
+    <permission android:name="android.permission.TEST_BIOMETRIC"
+        android:protectionLevel="signature" />
+
     <!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
     <permission android:name="android.permission.MANAGE_BIOMETRIC"
         android:protectionLevel="signature" />
@@ -4879,6 +4893,11 @@
     <permission android:name="android.permission.MANAGE_CONTENT_CAPTURE"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to manage the music recognition service.
+         @hide  <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_MUSIC_RECOGNITION"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to manage the content suggestions service.
          @hide  <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.MANAGE_CONTENT_SUGGESTIONS"
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index ffc5ff7..b9f0f58 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Controla el posicionamiento y el nivel de zoom de la pantalla."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Realizar gestos"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Puedes tocar y pellizcar la pantalla, deslizar el dedo y hacer otros gestos."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huellas digitales"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestos de huella digital"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Puede capturar los gestos realizados en el sensor de huellas digitales del dispositivo."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Hacer captura"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Puede hacer capturas de la pantalla."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a99d4d6..907fe5b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1658,11 +1658,11 @@
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"برای استفاده از <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید صدا را فشار دهید و سه ثانیه نگه دارید"</string>
     <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"ویژگی را انتخاب کنید که هنگام ضربه زدن روی دکمه دسترس‌پذیری استفاده می‌شود:"</string>
-    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند به بالا بکشید):"</string>
-    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند به بالا بکشید):"</string>
+    <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"ویژگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با دو انگشت صفحه را از پایین تند به‌بالا بکشید):"</string>
+    <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"ویزگی را برای استفاده با اشاره دسترس‌پذیری انتخاب کنید (با سه انگشت صفحه را از پایین تند به‌بالا بکشید):"</string>
     <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"برای جابه‌جایی بین ویژگی‌ها، دکمه دسترس‌پذیری را لمس کنید و نگه دارید."</string>
-    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"برای جابه‌جایی بین ویژگی‌ها، با دو انگشت صفحه را تند به بالا بکشید و نگه دارید."</string>
-    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابه‌جایی بین ویژگی‌ها، با سه انگشت صفحه را تند به بالا بکشید و نگه دارید."</string>
+    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"برای جابه‌جایی بین ویژگی‌ها، با دو انگشت صفحه را تند به‌بالا بکشید و نگه دارید."</string>
+    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"برای جابه‌جایی بین ویژگی‌ها، با سه انگشت صفحه را تند به‌بالا بکشید و نگه دارید."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"درشت‌نمایی"</string>
     <string name="user_switched" msgid="7249833311585228097">"کاربر کنونی <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="1912993630661332336">"در حالت تغییر به <xliff:g id="NAME">%1$s</xliff:g>…"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 59d4d77..d2bdbbb 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"Contrôle le niveau de zoom et le positionnement de l\'écran."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"Effectuer des gestes"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"Permet d\'appuyer sur l\'écran, de le balayer, de le pincer et d\'effectuer d\'autres gestes."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes avec l\'empreinte digitale"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"Gestes d\'empreinte digitale"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"Peut enregistrer des gestes effectués sur le lecteur d\'empreinte digitale de l\'appareil."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"Prendre une capture d\'écran"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"Peut prendre des captures d\'écran."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index fcf64c1..ffe073c 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -327,7 +327,7 @@
     <string name="capability_desc_canControlMagnification" msgid="2206586716709254805">"ഡിസ്പ്ലേയുടെ സൂം നിലയും പൊസിഷനിംഗും നിയന്ത്രിക്കുക."</string>
     <string name="capability_title_canPerformGestures" msgid="9106545062106728987">"ജെസ്‌റ്ററുകൾ നിർവഹിക്കുക"</string>
     <string name="capability_desc_canPerformGestures" msgid="6619457251067929726">"ടാപ്പുചെയ്യാനോ സ്വൈപ്പുചെയ്യാനോ പിഞ്ചുചെയ്യാനോ മറ്റ് ജെസ്‌റ്ററുകൾ നിർവഹിക്കാനോ കഴിയും."</string>
-    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്‌റ്ററുകൾ"</string>
+    <string name="capability_title_canCaptureFingerprintGestures" msgid="1189053104594608091">"ഫിംഗർപ്രിന്റ് ജെസ്‌ച്ചറുകൾ"</string>
     <string name="capability_desc_canCaptureFingerprintGestures" msgid="6861869337457461274">"ഉപകരണത്തിന്റെ ഫിംഗർപ്രിന്റ് സെൻസറിൽ ചെയ്‌ത ജെസ്‌റ്ററുകൾ ക്യാപ്‌ചർ ചെയ്യാനാകും."</string>
     <string name="capability_title_canTakeScreenshot" msgid="3895812893130071930">"സ്ക്രീന്‍ഷോട്ട് എടുക്കുക"</string>
     <string name="capability_desc_canTakeScreenshot" msgid="7762297374317934052">"ഡിസ്‌പ്ലേയുടെ സ്‌ക്രീൻഷോട്ട് എടുക്കാൻ കഴിയും."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 01ee79a9..2cdf709 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1477,7 +1477,7 @@
     <string name="add_account_button_label" msgid="322390749416414097">"खाता थप्नुहोस्"</string>
     <string name="number_picker_increment_button" msgid="7621013714795186298">"बढाउनुहोस्"</string>
     <string name="number_picker_decrement_button" msgid="5116948444762708204">"घटाउनुहोस्"</string>
-    <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> छोइराख्नुहोस्।"</string>
+    <string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"बढाउन माथि र घटाउन तल सार्नुहोस्।"</string>
     <string name="time_picker_increment_minute_button" msgid="7195870222945784300">"मिनेट बढाउनुहोस्"</string>
     <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"मिनेट घटाउनुहोस्"</string>
@@ -1660,9 +1660,9 @@
     <string name="accessibility_button_prompt_text" msgid="8343213623338605305">"तपाईंले पहुँचको बटन ट्याप गर्दा प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस्:"</string>
     <string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (दुईवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
     <string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"तपाईंले पहुँचको इसारामार्फत प्रयोग गर्न चाहनुभएको सुविधा छनौट गर्नुहोस् (तीनवटा औँलाले स्क्रिनको फेदबाट माथितिर स्वाइप गर्नुहोस्):"</string>
-    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन छोइराख्नुहोस्।"</string>
-    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</string>
-    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्।"</string>
+    <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"एउटा सुविधाबाट अर्को सुविधामा जान पहुँच बटन टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"एउटा सुविधाबाट अर्को सुविधामा जान दुईवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"एउटा सुविधाबाट अर्को सुविधामा जान तीनवटा औँलाले माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"म्याग्निफिकेसन"</string>
     <string name="user_switched" msgid="7249833311585228097">"अहिलेको प्रयोगकर्ता <xliff:g id="NAME">%1$s</xliff:g>।"</string>
     <string name="user_switching_message" msgid="1912993630661332336">"<xliff:g id="NAME">%1$s</xliff:g> मा स्विच गर्दै..."</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6ea5f52..11bfd78 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"ਆਪਣੀਆਂ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"ਐਪ ਨੂੰ ਗਲੋਬਲ ਆਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਅਵਾਜ਼ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">" ਆਡੀਓ  ਰਿਕਾਰਡ ਕਰਨ"</string>
-    <!-- no translation found for permdesc_recordAudio (5857246765327514062) -->
-    <skip />
-    <!-- no translation found for permlab_recordBackgroundAudio (5891032812308878254) -->
-    <skip />
-    <!-- no translation found for permdesc_recordBackgroundAudio (1992623135737407516) -->
-    <skip />
+    <string name="permdesc_recordAudio" msgid="5857246765327514062">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਵਰਤ ਕੇ ਆਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣਨਾ"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"ਇਹ ਐਪ ਤੁਹਾਡੀ ਸਰੀਰਕ ਸਰਗਰਮੀ ਨੂੰ ਪਛਾਣ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_camera" msgid="6320282492904119413">"ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਓ"</string>
-    <!-- no translation found for permdesc_camera (5240801376168647151) -->
-    <skip />
-    <!-- no translation found for permlab_backgroundCamera (7549917926079731681) -->
-    <skip />
-    <!-- no translation found for permdesc_backgroundCamera (1615291686191138250) -->
-    <skip />
+    <string name="permdesc_camera" msgid="5240801376168647151">"ਇਹ ਐਪ ਵਰਤੋਂ ਵਿੱਚ ਹੋਣ ਵੇਲੇ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਤਸਵੀਰਾਂ ਖਿੱਚੋ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"ਇਹ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ।"</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਤਸਵੀਰਾਂ ਅਤੇ ਵੀਡੀਓ ਬਣਾਉਣ ਲਈ ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਤੱਕ ਪਹੁੰਚ ਦਿਓ"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"ਇਹ ਵਿਸ਼ੇਸ਼ ਅਧਿਕ੍ਰਿਤ ਜਾਂ ਸਿਸਟਮ ਐਪ ਕਿਸੇ ਵੇਲੇ ਵੀ ਸਿਸਟਮ ਕੈਮਰੇ ਨੂੰ ਵਰਤ ਕੇ ਤਸਵੀਰਾਂ ਖਿੱਚ ਸਕਦੀ ਹੈ ਅਤੇ ਵੀਡੀਓ ਫ਼ਾਈਲਾਂ ਰਿਕਾਰਡ ਕਰ ਸਕਦੀ ਹੈ। ਐਪ ਨੂੰ ਵੀ android.permission.CAMERA ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"ਐਪਲੀਕੇਸ਼ਨ ਜਾਂ ਸੇਵਾ ਨੂੰ ਕੈਮਰਾ ਡੀਵਾਈਸਾਂ ਦੇ ਚਾਲੂ ਜਾਂ ਬੰਦ ਕੀਤੇ ਜਾਣ ਬਾਰੇ ਕਾਲਬੈਕ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ।"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0c15f9e..da41508 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1137,7 +1137,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="1532369154488982046">"Вибрати все"</string>
-    <string name="cut" msgid="2561199725874745819">"Виріз."</string>
+    <string name="cut" msgid="2561199725874745819">"Вирізати"</string>
     <string name="copy" msgid="5472512047143665218">"Копіювати"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Не вдалося скопіювати в буфер обміну"</string>
     <string name="paste" msgid="461843306215520225">"Вставити"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0cfbf6d..e434ac9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3660,6 +3660,8 @@
     -->
     <string name="config_defaultContentSuggestionsService" translatable="false"></string>
 
+    <string name="config_defaultMusicRecognitionService" translatable="false"></string>
+
     <!-- The package name for the default retail demo app.
          This package must be trusted, as it has the permissions to query the usage stats on the
          device.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6009689..1249b17 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3454,6 +3454,7 @@
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
   <java-symbol type="string" name="config_defaultAppPredictionService" />
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
+  <java-symbol type="string" name="config_defaultMusicRecognitionService" />
   <java-symbol type="string" name="config_defaultAttentionService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsService" />
   <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6f55b88..38dce15 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -164,6 +164,14 @@
                 <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
             </intent-filter>
         </activity>
+        <activity android:name="android.view.ViewInputConnectionTestActivity"
+                  android:label="View Input Connection Test"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
         <activity android:name="StubTestBrowserActivity"
             android:label="Stubbed Test Browser"
             android:exported="true">
diff --git a/core/tests/coretests/res/layout/activity_view_ic_test.xml b/core/tests/coretests/res/layout/activity_view_ic_test.xml
new file mode 100644
index 0000000..fa26869
--- /dev/null
+++ b/core/tests/coretests/res/layout/activity_view_ic_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/root"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 7fa1613..3df0a68 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -117,8 +117,8 @@
         history.addNotificationToWrite(n);
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(2);
-        assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n2);
-        assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
+        assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n2);
+        assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
         assertThat(history.getHistoryCount()).isEqualTo(2);
     }
 
@@ -141,11 +141,11 @@
         history.addNotificationsToWrite(secondHistory);
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(5);
-        assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n3);
-        assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
-        assertThat(history.getNotificationsToWrite().get(2)).isSameAs(n4);
-        assertThat(history.getNotificationsToWrite().get(3)).isSameAs(n2);
-        assertThat(history.getNotificationsToWrite().get(4)).isSameAs(n5);
+        assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n3);
+        assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
+        assertThat(history.getNotificationsToWrite().get(2)).isSameInstanceAs(n4);
+        assertThat(history.getNotificationsToWrite().get(3)).isSameInstanceAs(n2);
+        assertThat(history.getNotificationsToWrite().get(4)).isSameInstanceAs(n5);
         assertThat(history.getHistoryCount()).isEqualTo(5);
 
         assertThat(history.getPooledStringsToWrite()).asList().contains(n2.getChannelName());
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index ba060fa..593e70e 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -45,7 +45,8 @@
                         CompoundFormula.AND, Arrays.asList(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2));
 
         assertThat(compoundFormula.getConnector()).isEqualTo(CompoundFormula.AND);
-        assertThat(compoundFormula.getFormulas()).containsAllOf(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
+        assertThat(compoundFormula.getFormulas())
+                .containsAtLeast(ATOMIC_FORMULA_1, ATOMIC_FORMULA_2);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
index d45fee9..9ad63ad 100644
--- a/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
+++ b/core/tests/coretests/src/android/content/pm/parsing/result/ParseInputAndResultTest.kt
@@ -113,7 +113,7 @@
         assertError(result)
         assertThat(result.errorCode).isEqualTo(errorCode)
         assertThat(result.errorMessage).isEqualTo(errorMessage)
-        assertThat(result.exception).isSameAs(exception)
+        assertThat(result.exception).isSameInstanceAs(exception)
     }
 
     @Test
@@ -125,13 +125,13 @@
         assertError(result)
         assertThat(result.errorCode).isEqualTo(errorCode)
         assertThat(result.errorMessage).isEqualTo(errorMessage)
-        assertThat(result.exception).isSameAs(exception)
+        assertThat(result.exception).isSameInstanceAs(exception)
 
         val carriedResult = input.error<Int>(result)
         assertError(carriedResult)
         assertThat(carriedResult.errorCode).isEqualTo(errorCode)
         assertThat(carriedResult.errorMessage).isEqualTo(errorMessage)
-        assertThat(carriedResult.exception).isSameAs(exception)
+        assertThat(carriedResult.exception).isSameInstanceAs(exception)
     }
 
     @Test
@@ -259,7 +259,7 @@
     private fun assertSuccess(expected: Any? = null, result: ParseResult<*>) {
         assertThat(result.isError).isFalse()
         assertThat(result.isSuccess).isTrue()
-        assertThat(result.result).isSameAs(expected)
+        assertThat(result.result).isSameInstanceAs(expected)
         assertThat(result.errorCode).isEqualTo(PackageManager.INSTALL_SUCCEEDED)
         assertThat(result.errorMessage).isNull()
         assertThat(result.exception).isNull()
diff --git a/core/tests/coretests/src/android/view/ViewInputConnectionTest.java b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
new file mode 100644
index 0000000..d667af3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTest.java
@@ -0,0 +1,292 @@
+/*
+ * 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 android.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.text.format.DateUtils;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Button;
+import android.widget.EditText;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for internal APIs/behaviors of {@link View} and {@link InputConnection}.
+ */
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ViewInputConnectionTest {
+    @Rule
+    public ActivityTestRule<ViewInputConnectionTestActivity> mActivityRule =
+            new ActivityTestRule<>(ViewInputConnectionTestActivity.class);
+
+    private Instrumentation mInstrumentation;
+    private ViewInputConnectionTestActivity mActivity;
+    private InputMethodManager mImm;
+
+    @Before
+    public void before() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(5 * DateUtils.SECOND_IN_MILLIS, mActivity::hasWindowFocus);
+        assertTrue(mActivity.hasWindowFocus());
+        mImm = mActivity.getSystemService(InputMethodManager.class);
+    }
+
+    @Test
+    public void testInputConnectionCallbacks() throws Throwable {
+        // Add two EditText inputs to the layout view.
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestEditText editText1 = new TestEditText(mActivity, false);
+        final TestEditText editText2 = new TestEditText(mActivity, false);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(editText1);
+            viewGroup.addView(editText2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Focus into the first EditText.
+        mActivityRule.runOnUiThread(editText1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isTrue();
+        assertThat(editText2.isFocused()).isFalse();
+
+        // Show the IME for the first EditText. Assert that the appropriate opened/closed callbacks
+        // have been invoked (InputConnection opened for the first EditText).
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Focus into the second EditText.
+        mActivityRule.runOnUiThread(editText2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isFalse();
+        assertThat(editText2.isFocused()).isTrue();
+
+        // Show the IME for the second EditText. Assert that the appropriate opened/closed callbacks
+        // have been invoked (InputConnection closed for the first EditText and opened for the
+        // second EditText).
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isTrue();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    @Test
+    public void testInputConnectionCallbacks_nullInputConnection() throws Throwable {
+        // Add two EditText inputs to the layout view.
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestEditText editText1 = new TestEditText(mActivity, true);
+        final TestEditText editText2 = new TestEditText(mActivity, true);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(editText1);
+            viewGroup.addView(editText2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Focus into the first EditText.
+        mActivityRule.runOnUiThread(editText1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isTrue();
+        assertThat(editText2.isFocused()).isFalse();
+
+        // Show the IME for the first EditText. Assert that the opened/closed callbacks are not
+        // invoked since there's no input connection.
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText1, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Focus into the second EditText.
+        mActivityRule.runOnUiThread(editText2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(editText1.isFocused()).isFalse();
+        assertThat(editText2.isFocused()).isTrue();
+
+        // Show the IME for the second EditText. Assert that the opened/closed callbacks are not
+        // invoked since there's no input connection.
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(editText2, 0));
+        mInstrumentation.waitForIdleSync();
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(editText1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(editText2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(editText2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(editText2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    @Test
+    public void testInputConnectionCallbacks_nonEditableInput() throws Throwable {
+        final ViewGroup viewGroup = mActivity.findViewById(R.id.root);
+        final TestButton view1 = new TestButton(mActivity);
+        final TestButton view2 = new TestButton(mActivity);
+        mActivityRule.runOnUiThread(() -> {
+            viewGroup.addView(view1);
+            viewGroup.addView(view2);
+        });
+        mInstrumentation.waitForIdleSync();
+
+        // Request focus + IME on the first view.
+        mActivityRule.runOnUiThread(view1::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(view1.isFocused()).isTrue();
+        assertThat(view2.isFocused()).isFalse();
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+        mInstrumentation.waitForIdleSync();
+
+        // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(view2.mCalledOnCreateInputConnection).isFalse();
+            assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+        });
+
+        // Request focus + IME on the second view.
+        mActivityRule.runOnUiThread(view2::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertThat(view1.isFocused()).isFalse();
+        assertThat(view2.isFocused()).isTrue();
+        mActivityRule.runOnUiThread(() -> mImm.showSoftInput(view1, 0));
+        mInstrumentation.waitForIdleSync();
+
+        // Assert that the opened/closed callbacks are not invoked since there's no InputConnection.
+        mActivityRule.runOnUiThread(() -> {
+            assertThat(view1.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view1.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view1.mCalledOnInputConnectionClosed).isFalse();
+
+            assertThat(view2.mCalledOnCreateInputConnection).isTrue();
+            assertThat(view2.mCalledOnInputConnectionOpened).isFalse();
+            assertThat(view2.mCalledOnInputConnectionClosed).isFalse();
+        });
+    }
+
+    private static class TestEditText extends EditText {
+        private final boolean mReturnNullInputConnection;
+
+        public boolean mCalledOnCreateInputConnection = false;
+        public boolean mCalledOnInputConnectionOpened = false;
+        public boolean mCalledOnInputConnectionClosed = false;
+
+        TestEditText(Context context, boolean returnNullInputConnection) {
+            super(context);
+            mReturnNullInputConnection = returnNullInputConnection;
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            mCalledOnCreateInputConnection = true;
+            if (mReturnNullInputConnection) {
+                return null;
+            } else {
+                return super.onCreateInputConnection(outAttrs);
+            }
+        }
+
+        @Override
+        public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+                @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+            mCalledOnInputConnectionOpened = true;
+            super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+        }
+
+        @Override
+        public void onInputConnectionClosedInternal() {
+            mCalledOnInputConnectionClosed = true;
+            super.onInputConnectionClosedInternal();
+        }
+    }
+
+    private static class TestButton extends Button {
+        public boolean mCalledOnCreateInputConnection = false;
+        public boolean mCalledOnInputConnectionOpened = false;
+        public boolean mCalledOnInputConnectionClosed = false;
+
+        TestButton(Context context) {
+            super(context);
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            mCalledOnCreateInputConnection = true;
+            return super.onCreateInputConnection(outAttrs);
+        }
+
+        @Override
+        public void onInputConnectionOpenedInternal(@NonNull InputConnection inputConnection,
+                @NonNull EditorInfo editorInfo, @Nullable Handler handler) {
+            mCalledOnInputConnectionOpened = true;
+            super.onInputConnectionOpenedInternal(inputConnection, editorInfo, handler);
+        }
+
+        @Override
+        public void onInputConnectionClosedInternal() {
+            mCalledOnInputConnectionClosed = true;
+            super.onInputConnectionClosedInternal();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
similarity index 60%
copy from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
copy to core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
index 74cae02..55c812d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
+++ b/core/tests/coretests/src/android/view/ViewInputConnectionTestActivity.java
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package android.view;
 
-/**
- * Interface for under-display fingerprint sensors.
- * {@link com.android.server.biometrics.sensors.ClientMonitor} subclass that require knowledge of
- * finger position (e.g. enroll, authenticate) should implement this.
- */
-public interface Udfps {
-    void onFingerDown(int x, int y, float minor, float major);
-    void onFingerUp();
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+public class ViewInputConnectionTestActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_view_ic_test);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 628252d..402b92a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -52,7 +52,8 @@
 
     @Test
     public void testGetLocalTextClassifier() {
-        assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL)).isSameAs(TextClassifier.NO_OP);
+        assertThat(mTcm.getTextClassifier(TextClassifier.LOCAL))
+                .isSameInstanceAs(TextClassifier.NO_OP);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
index f108eb8..a2bc77a 100644
--- a/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
+++ b/core/tests/coretests/src/com/android/internal/infra/AndroidFutureTest.java
@@ -81,7 +81,7 @@
         future.completeExceptionally(origException);
         ExecutionException executionException =
                 expectThrows(ExecutionException.class, future::get);
-        assertThat(executionException.getCause()).isSameAs(origException);
+        assertThat(executionException.getCause()).isSameInstanceAs(origException);
     }
 
     @Test
@@ -92,7 +92,7 @@
         CountDownLatch latch = new CountDownLatch(1);
         future.whenComplete((obj, err) -> {
             assertThat(obj).isNull();
-            assertThat(err).isSameAs(origException);
+            assertThat(err).isSameInstanceAs(origException);
             latch.countDown();
         });
         latch.await();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 5914887..942045c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -107,7 +107,7 @@
             if (!isAlive) {
                 return false;
             }
-            assertThat(mRecipient).isSameAs(recipient);
+            assertThat(mRecipient).isSameInstanceAs(recipient);
             mRecipient = null;
             return true;
         }
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
index 9f68ef3..7eca320 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/RegisterStatusBarResultTest.java
@@ -74,7 +74,7 @@
         assertThat(copy.mImeBackDisposition).isEqualTo(original.mImeBackDisposition);
         assertThat(copy.mShowImeSwitcher).isEqualTo(original.mShowImeSwitcher);
         assertThat(copy.mDisabledFlags2).isEqualTo(original.mDisabledFlags2);
-        assertThat(copy.mImeToken).isSameAs(original.mImeToken);
+        assertThat(copy.mImeToken).isSameInstanceAs(original.mImeToken);
         assertThat(copy.mNavbarColorManagedByIme).isEqualTo(original.mNavbarColorManagedByIme);
         assertThat(copy.mAppFullscreen).isEqualTo(original.mAppFullscreen);
         assertThat(copy.mAppImmersive).isEqualTo(original.mAppImmersive);
diff --git a/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
new file mode 100644
index 0000000..88bbcc2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/view/RecyclerViewCaptureHelperTest.java
@@ -0,0 +1,343 @@
+/*
+ * 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.internal.view;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+import com.google.common.truth.Truth;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class RecyclerViewCaptureHelperTest {
+    private static final int CHILD_VIEWS = 12;
+    private static final int CHILD_VIEW_HEIGHT = 300;
+    private static final int WINDOW_WIDTH = 800;
+    private static final int WINDOW_HEIGHT = 1200;
+    private static final int CAPTURE_HEIGHT = 600;
+
+    private FrameLayout mParent;
+    private RecyclerView mTarget;
+    private WindowManager mWm;
+
+    private WindowManager.LayoutParams mWindowLayoutParams;
+
+    private Context mContext;
+    private float mDensity;
+    private LinearLayoutManager mLinearLayoutManager;
+    private Instrumentation mInstrumentation;
+
+    @Before
+    @UiThreadTest
+    public void setUp() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mContext = mInstrumentation.getContext();
+        mDensity = mContext.getResources().getDisplayMetrics().density;
+
+        mParent = new FrameLayout(mContext);
+
+        mTarget = new RecyclerView(mContext);
+        mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+        mTarget.setAdapter(new TestAdapter());
+        mLinearLayoutManager =
+                new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
+        mTarget.setLayoutManager(mLinearLayoutManager);
+        mWm = mContext.getSystemService(WindowManager.class);
+
+        // Setup the window that we are going to use
+        mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT,
+                TYPE_APPLICATION_OVERLAY, FLAG_NOT_TOUCHABLE, PixelFormat.OPAQUE);
+        mWindowLayoutParams.setTitle("ScrollViewCaptureHelper");
+        mWindowLayoutParams.gravity = Gravity.CENTER;
+        mWm.addView(mParent, mWindowLayoutParams);
+    }
+
+    @After
+    @UiThreadTest
+    public void tearDown() {
+        mWm.removeViewImmediate(mParent);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromTop() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+        // mTarget.createSnapshot(new ViewDebug.HardwareCanvasProvider(), false);
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+                scrollBounds, request);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further up to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromTop() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        assertThat(scrollBounds.height()).isGreaterThan(CAPTURE_HEIGHT);
+
+        // Capture between y = +1200 to +1800 pixels BELOW current top
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromMiddle() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromMiddle() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_up_fromBottom() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0);
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+        assertThat(request).isEqualTo(scrollResult.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(-CAPTURE_HEIGHT);
+        assertAvailableAreaCompletelyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_down_fromBottom() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 3);
+
+        RecyclerViewCaptureHelper rvc = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = rvc.onComputeScrollBounds(mTarget);
+        rvc.onPrepareForStart(mTarget, scrollBounds);
+
+        Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(),
+                WINDOW_HEIGHT + CAPTURE_HEIGHT);
+
+        ScrollResult scrollResult = rvc.onScrollRequested(mTarget,
+                scrollBounds, request);
+        Truth.assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+        // The result is an empty rectangle and no scrolling, since it
+        // is not possible to physically scroll further down to make the
+        // requested area visible at all (it doesn't exist).
+        assertEmpty(scrollResult.availableArea);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offTopEdge() {
+        mTarget.scrollBy(0, -(WINDOW_HEIGHT * 3));
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the top of the content
+        //from -1500 to -900, (starting at 1200 = -300 to +300 within the content)
+        int top = 0;
+        Rect request = new Rect(
+                0, top - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), top + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult scrollResult = helper.onScrollRequested(mTarget, scrollBounds, request);
+        assertThat(request).isEqualTo(scrollResult.requestedArea);
+
+        ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+        // The result is a partial result
+        Rect expectedResult = new Rect(request);
+        expectedResult.top += (CAPTURE_HEIGHT / 2); // top half clipped
+        assertThat(expectedResult).isEqualTo(result.availableArea);
+        assertThat(scrollResult.scrollDelta).isEqualTo(0);
+        assertAvailableAreaPartiallyVisible(scrollResult, mTarget);
+    }
+
+    @Test
+    @UiThreadTest
+    public void onScrollRequested_offBottomEdge() {
+        mTarget.scrollBy(0, WINDOW_HEIGHT * 2);
+
+        RecyclerViewCaptureHelper helper = new RecyclerViewCaptureHelper();
+        Rect scrollBounds = helper.onComputeScrollBounds(mTarget);
+        helper.onPrepareForStart(mTarget, scrollBounds);
+
+        // Create a request which lands halfway off the bottom of the content
+        //from 600 to to 1200, (starting at 2400 = 3000 to  3600 within the content)
+
+        int bottom = WINDOW_HEIGHT;
+        Rect request = new Rect(
+                0, bottom - (CAPTURE_HEIGHT / 2),
+                scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2));
+
+        ScrollResult result = helper.onScrollRequested(mTarget, scrollBounds, request);
+
+        Rect expectedResult = new Rect(request);
+        expectedResult.bottom -= 300; // bottom half clipped
+        assertThat(expectedResult).isEqualTo(result.availableArea);
+        assertThat(result.scrollDelta).isEqualTo(0);
+        assertAvailableAreaPartiallyVisible(result, mTarget);
+    }
+
+    static final class TestViewHolder extends RecyclerView.ViewHolder {
+        TestViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+    static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        private final Random mRandom = new Random();
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new TestViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView;
+            view.setText("Child #" + position);
+            view.setTextColor(Color.WHITE);
+            view.setTextSize(30f);
+            view.setBackgroundColor(Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(),
+                    mRandom.nextFloat()));
+            view.setMinHeight(CHILD_VIEW_HEIGHT);
+        }
+
+        @Override
+        public int getItemCount() {
+            return CHILD_VIEWS;
+        }
+    }
+
+    static void assertEmpty(Rect r) {
+        if (r != null && !r.isEmpty()) {
+            fail("Not true that " + r + " is empty");
+        }
+    }
+
+    static Rect getVisibleRect(View v) {
+        Rect r = new Rect(0, 0, v.getWidth(), v.getHeight());
+        v.getLocalVisibleRect(r);
+        return r;
+    }
+
+    static void assertAvailableAreaCompletelyVisible(ScrollResult result, View container) {
+        Rect requested = new Rect(result.availableArea);
+        requested.offset(0, -result.scrollDelta); // make relative
+        Rect localVisible = getVisibleRect(container);
+        if (!localVisible.contains(requested)) {
+            fail("Not true that all of " + requested + " is contained by " + localVisible);
+        }
+    }
+
+    static void assertAvailableAreaPartiallyVisible(ScrollResult result, View container) {
+        Rect requested = new Rect(result.availableArea);
+        requested.offset(0, -result.scrollDelta); // make relative
+        Rect localVisible = getVisibleRect(container);
+        if (!Rect.intersects(localVisible, requested)) {
+            fail("Not true that any of " + requested + " is contained by " + localVisible);
+        }
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/Android.bp b/core/tests/powertests/PowerStatsViewer/Android.bp
new file mode 100644
index 0000000..a3dc4fb
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/Android.bp
@@ -0,0 +1,13 @@
+android_test {
+    name: "PowerStatsViewer",
+    srcs: ["src/**/*.java"],
+    defaults: ["SettingsLibDefaults"],
+    static_libs: [
+        "androidx.appcompat_appcompat",
+        "androidx.cardview_cardview",
+        "androidx.recyclerview_recyclerview",
+        "com.google.android.material_material",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
new file mode 100644
index 0000000..378d035
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.core.powerstatsviewer"
+          android:sharedUserId="android.uid.system">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BATTERY_STATS"/>
+
+    <application
+        android:theme="@style/Theme"
+        android:label="Power Stats Viewer">
+        <activity android:name=".PowerStatsViewerActivity"
+                  android:label="Power Stats Viewer"
+                  android:launchMode="singleTop"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".AppPickerActivity"
+                  android:label="Power Stats - Select an App"/>
+
+    </application>
+</manifest>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
new file mode 100644
index 0000000..fe6fe2d
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_info_layout.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="?android:attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/secondary_app_icon_size"
+        android:layout_height="@dimen/secondary_app_icon_size"
+        android:layout_marginEnd="12dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="4dp"
+        android:gravity="start|center_vertical"/>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+        <TextView
+            android:id="@+id/uid"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+        <TextView
+            android:id="@+id/packages"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textDirection="locale"
+            android:maxLines="3"
+            android:ellipsize="end"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textColor="?android:attr/textColorSecondary"/>
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/power_mah"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="end|center_vertical"
+        android:layout_marginStart="16dp"
+        android:gravity="right"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:visibility="gone"/>
+
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
new file mode 100644
index 0000000..6f28999
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/app_picker_layout.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/app_list_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"/>
+
+    <ProgressBar
+        style="?android:attr/progressBarStyleLarge"
+        android:id="@+id/loading_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:indeterminate="true"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
new file mode 100644
index 0000000..1ced825
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_entry_layout.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:paddingTop="8dp"
+    android:paddingBottom="8dp">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearanceBody"/>
+
+    <TextView
+        android:id="@+id/amount"
+        android:layout_width="0dp"
+        android:layout_weight="0.7"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:textAppearance="@style/TextAppearanceBody"/>
+
+    <TextView
+        android:id="@+id/percent"
+        android:layout_width="64dp"
+        android:layout_height="wrap_content"
+        android:gravity="right"
+        android:textAppearance="@style/TextAppearanceBody"/>
+</LinearLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
new file mode 100644
index 0000000..9949418
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/layout/power_stats_viewer_layout.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.cardview.widget.CardView
+            style="@style/LoadTestCardView"
+            android:id="@+id/app_card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:layout_marginEnd="10dp"
+            android:layout_marginBottom="10dp"
+            android:layout_marginStart="10dp"
+            android:padding="20dp">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:minHeight="80dp"
+                android:paddingStart="10dp"
+                android:paddingEnd="10dp">
+
+                <include layout="@layout/app_info_layout"/>
+
+            </LinearLayout>
+        </androidx.cardview.widget.CardView>
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/power_stats_data_view"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"/>
+
+        <TextView
+            android:id="@+id/empty_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center"
+            android:visibility="gone"
+            android:text="No power stats available"/>
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/loading_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#AAFFFFFF">
+        <ProgressBar
+            style="?android:attr/progressBarStyleLarge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:indeterminate="true"/>
+    </FrameLayout>
+</FrameLayout>
diff --git a/core/tests/powertests/PowerStatsViewer/res/values/styles.xml b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
new file mode 100644
index 0000000..629d729
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ 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.
+  -->
+
+<resources>
+    <style name="Theme" parent="Theme.MaterialComponents.Light">
+        <item name="colorPrimary">#34a853</item>
+        <item name="android:windowActionBar">true</item>
+        <item name="android:windowNoTitle">false</item>
+    </style>
+
+    <style name="LoadTestCardView" parent="Widget.MaterialComponents.CardView">
+        <item name="cardBackgroundColor">#ceead6</item>
+    </style>
+
+    <style name="TextAppearanceBody" parent="android:TextAppearance.DeviceDefault">
+        <item name="android:textColor">#000000</item>
+        <item name="android:textSize">18sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
new file mode 100644
index 0000000..8526561
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppInfoHelper.java
@@ -0,0 +1,114 @@
+/*
+ * 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.frameworks.core.powerstatsviewer;
+
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+
+class AppInfoHelper {
+
+    private static final String SYSTEM_SERVER_PACKAGE_NAME = "android";
+
+    public static class AppInfo {
+        public int uid;
+        public CharSequence label;
+        public double powerMah;
+        public ApplicationInfo iconInfo;
+        public CharSequence packages;
+    }
+
+    @Nullable
+    public static AppInfo makeApplicationInfo(PackageManager packageManager, int uid,
+            @Nullable BatterySipper sipper) {
+        if (sipper != null && sipper.drainType != BatterySipper.DrainType.APP) {
+            return null;
+        }
+
+        String packageWithHighestDrain = null;
+
+        AppInfo info = new AppInfo();
+        info.uid = uid;
+        if (sipper != null) {
+            sipper.sumPower();
+            info.powerMah = sipper.totalSmearedPowerMah;
+            packageWithHighestDrain = sipper.packageWithHighestDrain;
+        }
+        if (info.uid == Process.ROOT_UID) {
+            info.label = "<root>";
+        } else {
+            String[] packages = packageManager.getPackagesForUid(info.uid);
+            String primaryPackageName = null;
+            if (info.uid == Process.SYSTEM_UID) {
+                primaryPackageName = SYSTEM_SERVER_PACKAGE_NAME;
+            } else if (packages != null) {
+                for (String name : packages) {
+                    primaryPackageName = name;
+                    if (name.equals(packageWithHighestDrain)) {
+                        break;
+                    }
+                }
+            }
+
+            if (primaryPackageName != null) {
+                try {
+                    ApplicationInfo applicationInfo =
+                            packageManager.getApplicationInfo(primaryPackageName, 0);
+                    info.label = applicationInfo.loadLabel(packageManager);
+                    info.iconInfo = applicationInfo;
+                } catch (PackageManager.NameNotFoundException e) {
+                    info.label = primaryPackageName;
+                }
+            } else if (packageWithHighestDrain != null) {
+                info.label = packageWithHighestDrain;
+            }
+
+            if (packages != null && packages.length > 0) {
+                StringBuilder sb = new StringBuilder();
+                if (primaryPackageName != null) {
+                    sb.append(primaryPackageName);
+                }
+                for (String packageName : packages) {
+                    if (packageName.equals(primaryPackageName)) {
+                        continue;
+                    }
+
+                    if (sb.length() != 0) {
+                        sb.append(", ");
+                    }
+                    sb.append(packageName);
+                }
+
+                info.packages = sb;
+            }
+        }
+
+        // Default the app icon to System Server. This includes root, dex2oat and other UIDs.
+        if (info.iconInfo == null) {
+            try {
+                info.iconInfo =
+                        packageManager.getApplicationInfo(SYSTEM_SERVER_PACKAGE_NAME, 0);
+            } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+                // Won't happen
+            }
+        }
+        return info;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
new file mode 100644
index 0000000..b4fc73c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/AppPickerActivity.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2008 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.activity.result.contract.ActivityResultContract;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.frameworks.core.powerstatsviewer.AppInfoHelper.AppInfo;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Picker, showing a sorted list of applications consuming power.  Returns the selected
+ * application UID or Process.INVALID_UID.
+ */
+public class AppPickerActivity extends ComponentActivity {
+    private static final String TAG = "AppPicker";
+
+    public static final ActivityResultContract<Void, Integer> CONTRACT =
+            new ActivityResultContract<Void, Integer>() {
+                @NonNull
+                @Override
+                public Intent createIntent(@NonNull Context context, Void aVoid) {
+                    return new Intent(context, AppPickerActivity.class);
+                }
+
+                @Override
+                public Integer parseResult(int resultCode, @Nullable Intent intent) {
+                    if (resultCode != RESULT_OK || intent == null) {
+                        return Process.INVALID_UID;
+                    }
+                    return intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+                }
+            };
+
+    private AppListAdapter mAppListAdapter;
+    private RecyclerView mAppList;
+    private View mLoadingView;
+
+    private interface OnAppSelectedListener {
+        void onAppSelected(int uid);
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        getActionBar().setDisplayHomeAsUpEnabled(true);
+
+        setContentView(R.layout.app_picker_layout);
+
+        mLoadingView = findViewById(R.id.loading_view);
+
+        mAppList = findViewById(R.id.app_list_view);
+        mAppList.setLayoutManager(new LinearLayoutManager(this));
+        mAppListAdapter = new AppListAdapter(AppPickerActivity.this::setSelectedUid);
+        mAppList.setAdapter(mAppListAdapter);
+
+        LoaderManager.getInstance(this).initLoader(0, null,
+                new AppListLoaderCallbacks());
+    }
+
+    protected void setSelectedUid(int uid) {
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_UID, uid);
+        setResult(RESULT_OK, intent);
+        finish();
+    }
+
+    @Override
+    public boolean onNavigateUp() {
+        onBackPressed();
+        return true;
+    }
+
+    private static class AppListLoader extends AsyncLoaderCompat<List<AppInfo>> {
+        private final BatteryStatsHelper mStatsHelper;
+        private final UserManager mUserManager;
+        private final PackageManager mPackageManager;
+
+        AppListLoader(Context context) {
+            super(context);
+            mUserManager = context.getSystemService(UserManager.class);
+            mStatsHelper = new BatteryStatsHelper(context, false /* collectBatteryBroadcast */);
+            mStatsHelper.create((Bundle) null);
+            mStatsHelper.clearStats();
+            mPackageManager = context.getPackageManager();
+        }
+
+        @Override
+        public List<AppInfo> loadInBackground() {
+            List<AppInfo> applicationList = new ArrayList<>();
+
+            mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+                    mUserManager.getUserProfiles());
+
+            final List<BatterySipper> usageList = mStatsHelper.getUsageList();
+            for (BatterySipper sipper : usageList) {
+                AppInfo info =
+                        AppInfoHelper.makeApplicationInfo(mPackageManager, sipper.getUid(), sipper);
+                if (info != null) {
+                    applicationList.add(info);
+                }
+            }
+
+            applicationList.sort(
+                    Comparator.comparing((AppInfo a) -> a.powerMah).reversed());
+            return applicationList;
+        }
+
+        @Override
+        protected void onDiscardResult(List<AppInfo> result) {
+        }
+    }
+
+    private class AppListLoaderCallbacks implements
+            LoaderManager.LoaderCallbacks<List<AppInfo>> {
+
+        @NonNull
+        @Override
+        public Loader<List<AppInfo>> onCreateLoader(int id, Bundle args) {
+            return new AppListLoader(AppPickerActivity.this);
+        }
+
+        @Override
+        public void onLoadFinished(@NonNull Loader<List<AppInfo>> loader,
+                List<AppInfo> applicationList) {
+            mAppListAdapter.setApplicationList(applicationList);
+            mAppList.setVisibility(View.VISIBLE);
+            mLoadingView.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void onLoaderReset(@NonNull Loader<List<AppInfo>> loader) {
+        }
+    }
+
+    public class AppListAdapter extends RecyclerView.Adapter<AppViewHolder> {
+        private final OnAppSelectedListener mListener;
+        private List<AppInfo> mApplicationList;
+
+        public AppListAdapter(OnAppSelectedListener listener) {
+            mListener = listener;
+        }
+
+        void setApplicationList(List<AppInfo> applicationList) {
+            mApplicationList = applicationList;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mApplicationList.size();
+        }
+
+        @NonNull
+        @Override
+        public AppViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
+            LayoutInflater layoutInflater = LayoutInflater.from(viewGroup.getContext());
+            View view = layoutInflater.inflate(R.layout.app_info_layout, viewGroup, false);
+            return new AppViewHolder(view, mListener);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull AppViewHolder appViewHolder, int position) {
+            AppInfo item = mApplicationList.get(position);
+            appViewHolder.uid = item.uid;
+            appViewHolder.titleView.setText(item.label);
+            appViewHolder.uidView.setText(
+                    String.format(Locale.getDefault(), "UID: %d", item.uid));
+            appViewHolder.powerView.setText(
+                    String.format(Locale.getDefault(), "%.1f mAh", item.powerMah));
+            appViewHolder.iconView.setImageDrawable(
+                    item.iconInfo.loadIcon(getPackageManager()));
+            if (item.packages != null) {
+                appViewHolder.packagesView.setText(item.packages);
+                appViewHolder.packagesView.setVisibility(View.VISIBLE);
+            } else {
+                appViewHolder.packagesView.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    // View Holder used when displaying apps
+    public static class AppViewHolder extends RecyclerView.ViewHolder
+            implements View.OnClickListener {
+        private final OnAppSelectedListener mListener;
+
+        public int uid;
+        public TextView titleView;
+        public TextView uidView;
+        public ImageView iconView;
+        public TextView packagesView;
+        public TextView powerView;
+
+        AppViewHolder(View view, OnAppSelectedListener listener) {
+            super(view);
+            mListener = listener;
+            view.setOnClickListener(this);
+            titleView = view.findViewById(android.R.id.title);
+            uidView = view.findViewById(R.id.uid);
+            iconView = view.findViewById(android.R.id.icon);
+            packagesView = view.findViewById(R.id.packages);
+            powerView = view.findViewById(R.id.power_mah);
+            powerView.setVisibility(View.VISIBLE);
+        }
+
+        @Override
+        public void onClick(View v) {
+            mListener.onAppSelected(uid);
+        }
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
new file mode 100644
index 0000000..09f20ba
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java
@@ -0,0 +1,240 @@
+/*
+ * 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.os.Process;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PowerStatsData {
+    private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
+    private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
+    private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
+    private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
+            PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
+
+    enum EntryType {
+        POWER,
+        DURATION,
+    }
+
+    public static class Entry {
+        public String title;
+        public EntryType entryType;
+        public double value;
+        public double total;
+    }
+
+    private final AppInfoHelper.AppInfo mAppInfo;
+    private final List<Entry> mEntries = new ArrayList<>();
+
+    public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper,
+            int uid) {
+        List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
+
+        double totalPowerMah = 0;
+        double totalSmearedPowerMah = 0;
+        double totalPowerExcludeSystemMah = 0;
+        double totalScreenPower = 0;
+        double totalProportionalSmearMah = 0;
+        double totalCpuPowerMah = 0;
+        double totalSystemServiceCpuPowerMah = 0;
+        double totalUsagePowerMah = 0;
+        double totalWakeLockPowerMah = 0;
+        double totalMobileRadioPowerMah = 0;
+        double totalWifiPowerMah = 0;
+        double totalBluetoothPowerMah = 0;
+        double totalGpsPowerMah = 0;
+        double totalCameraPowerMah = 0;
+        double totalFlashlightPowerMah = 0;
+        double totalSensorPowerMah = 0;
+        double totalAudioPowerMah = 0;
+        double totalVideoPowerMah = 0;
+
+        long totalCpuTimeMs = 0;
+        long totalCpuFgTimeMs = 0;
+        long totalWakeLockTimeMs = 0;
+        long totalWifiRunningTimeMs = 0;
+        long totalBluetoothRunningTimeMs = 0;
+        long totalGpsTimeMs = 0;
+        long totalCameraTimeMs = 0;
+        long totalFlashlightTimeMs = 0;
+        long totalAudioTimeMs = 0;
+        long totalVideoTimeMs = 0;
+
+        BatterySipper uidSipper = null;
+        for (BatterySipper sipper : usageList) {
+            if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
+                totalScreenPower = sipper.sumPower();
+            }
+
+            if (isHiddenDrainType(sipper.drainType)) {
+                continue;
+            }
+
+            if (sipper.drainType == BatterySipper.DrainType.APP && sipper.getUid() == uid) {
+                uidSipper = sipper;
+            }
+
+            totalPowerMah += sipper.sumPower();
+            totalSmearedPowerMah += sipper.totalSmearedPowerMah;
+            totalProportionalSmearMah += sipper.proportionalSmearMah;
+
+            if (!isSystemSipper(sipper)) {
+                totalPowerExcludeSystemMah += sipper.totalSmearedPowerMah;
+            }
+
+            totalCpuPowerMah += sipper.cpuPowerMah;
+            totalSystemServiceCpuPowerMah += sipper.systemServiceCpuPowerMah;
+            totalUsagePowerMah += sipper.usagePowerMah;
+            totalWakeLockPowerMah += sipper.wakeLockPowerMah;
+            totalMobileRadioPowerMah += sipper.mobileRadioPowerMah;
+            totalWifiPowerMah += sipper.wifiPowerMah;
+            totalBluetoothPowerMah += sipper.bluetoothPowerMah;
+            totalGpsPowerMah += sipper.gpsPowerMah;
+            totalCameraPowerMah += sipper.cameraPowerMah;
+            totalFlashlightPowerMah += sipper.flashlightPowerMah;
+            totalSensorPowerMah += sipper.sensorPowerMah;
+            totalAudioPowerMah += sipper.audioPowerMah;
+            totalVideoPowerMah += sipper.videoPowerMah;
+
+            totalCpuTimeMs += sipper.cpuTimeMs;
+            totalCpuFgTimeMs += sipper.cpuFgTimeMs;
+            totalWakeLockTimeMs += sipper.wakeLockTimeMs;
+            totalWifiRunningTimeMs += sipper.wifiRunningTimeMs;
+            totalBluetoothRunningTimeMs += sipper.bluetoothRunningTimeMs;
+            totalGpsTimeMs += sipper.gpsTimeMs;
+            totalCameraTimeMs += sipper.cameraTimeMs;
+            totalFlashlightTimeMs += sipper.flashlightTimeMs;
+            totalAudioTimeMs += sipper.audioTimeMs;
+            totalVideoTimeMs += sipper.videoTimeMs;
+        }
+
+        mAppInfo = AppInfoHelper.makeApplicationInfo(context.getPackageManager(), uid, uidSipper);
+
+        if (uidSipper == null) {
+            return;
+        }
+
+        addEntry("Total power", EntryType.POWER,
+                uidSipper.totalSmearedPowerMah, totalSmearedPowerMah);
+        addEntry("... excluding system", EntryType.POWER,
+                uidSipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
+        addEntry("Screen, smeared", EntryType.POWER,
+                uidSipper.screenPowerMah, totalScreenPower);
+        addEntry("Other, smeared", EntryType.POWER,
+                uidSipper.proportionalSmearMah, totalProportionalSmearMah);
+        addEntry("Excluding smeared", EntryType.POWER,
+                uidSipper.totalPowerMah, totalPowerMah);
+        addEntry("CPU", EntryType.POWER,
+                uidSipper.cpuPowerMah, totalCpuPowerMah);
+        addEntry("System services", EntryType.POWER,
+                uidSipper.systemServiceCpuPowerMah, totalSystemServiceCpuPowerMah);
+        addEntry("RAM", EntryType.POWER,
+                uidSipper.usagePowerMah, totalUsagePowerMah);
+        addEntry("Wake lock", EntryType.POWER,
+                uidSipper.wakeLockPowerMah, totalWakeLockPowerMah);
+        addEntry("Mobile radio", EntryType.POWER,
+                uidSipper.mobileRadioPowerMah, totalMobileRadioPowerMah);
+        addEntry("WiFi", EntryType.POWER,
+                uidSipper.wifiPowerMah, totalWifiPowerMah);
+        addEntry("Bluetooth", EntryType.POWER,
+                uidSipper.bluetoothPowerMah, totalBluetoothPowerMah);
+        addEntry("GPS", EntryType.POWER,
+                uidSipper.gpsPowerMah, totalGpsPowerMah);
+        addEntry("Camera", EntryType.POWER,
+                uidSipper.cameraPowerMah, totalCameraPowerMah);
+        addEntry("Flashlight", EntryType.POWER,
+                uidSipper.flashlightPowerMah, totalFlashlightPowerMah);
+        addEntry("Sensors", EntryType.POWER,
+                uidSipper.sensorPowerMah, totalSensorPowerMah);
+        addEntry("Audio", EntryType.POWER,
+                uidSipper.audioPowerMah, totalAudioPowerMah);
+        addEntry("Video", EntryType.POWER,
+                uidSipper.videoPowerMah, totalVideoPowerMah);
+
+        addEntry("CPU time", EntryType.DURATION,
+                uidSipper.cpuTimeMs, totalCpuTimeMs);
+        addEntry("CPU foreground time", EntryType.DURATION,
+                uidSipper.cpuFgTimeMs, totalCpuFgTimeMs);
+        addEntry("Wake lock time", EntryType.DURATION,
+                uidSipper.wakeLockTimeMs, totalWakeLockTimeMs);
+        addEntry("WiFi running time", EntryType.DURATION,
+                uidSipper.wifiRunningTimeMs, totalWifiRunningTimeMs);
+        addEntry("Bluetooth time", EntryType.DURATION,
+                uidSipper.bluetoothRunningTimeMs, totalBluetoothRunningTimeMs);
+        addEntry("GPS time", EntryType.DURATION,
+                uidSipper.gpsTimeMs, totalGpsTimeMs);
+        addEntry("Camera time", EntryType.DURATION,
+                uidSipper.cameraTimeMs, totalCameraTimeMs);
+        addEntry("Flashlight time", EntryType.DURATION,
+                uidSipper.flashlightTimeMs, totalFlashlightTimeMs);
+        addEntry("Audio time", EntryType.DURATION,
+                uidSipper.audioTimeMs, totalAudioTimeMs);
+        addEntry("Video time", EntryType.DURATION,
+                uidSipper.videoTimeMs, totalVideoTimeMs);
+    }
+
+    protected boolean isHiddenDrainType(BatterySipper.DrainType drainType) {
+        return drainType == BatterySipper.DrainType.IDLE
+                || drainType == BatterySipper.DrainType.CELL
+                || drainType == BatterySipper.DrainType.SCREEN
+                || drainType == BatterySipper.DrainType.UNACCOUNTED
+                || drainType == BatterySipper.DrainType.OVERCOUNTED
+                || drainType == BatterySipper.DrainType.BLUETOOTH
+                || drainType == BatterySipper.DrainType.WIFI;
+    }
+
+    private boolean isSystemSipper(BatterySipper sipper) {
+        final int uid = sipper.uidObj == null ? -1 : sipper.getUid();
+        if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+            return true;
+        } else if (sipper.mPackages != null) {
+            for (final String packageName : sipper.mPackages) {
+                for (final String systemPackage : PACKAGES_SYSTEM) {
+                    if (systemPackage.equals(packageName)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    private void addEntry(String title, EntryType entryType, double amount, double totalAmount) {
+        Entry entry = new Entry();
+        entry.title = title;
+        entry.entryType = entryType;
+        entry.value = amount;
+        entry.total = totalAmount;
+        mEntries.add(entry);
+    }
+
+    public AppInfoHelper.AppInfo getAppInfo() {
+        return mAppInfo;
+    }
+
+    public List<Entry> getEntries() {
+        return mEntries;
+    }
+}
diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
new file mode 100644
index 0000000..1605e9c
--- /dev/null
+++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java
@@ -0,0 +1,263 @@
+/*
+ * 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.frameworks.core.powerstatsviewer;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.activity.ComponentActivity;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.app.LoaderManager.LoaderCallbacks;
+import androidx.loader.content.Loader;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settingslib.utils.AsyncLoaderCompat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+public class PowerStatsViewerActivity extends ComponentActivity {
+    private static final int POWER_STATS_REFRESH_RATE_MILLIS = 60 * 1000;
+    public static final String PREF_SELECTED_UID = "selectedUid";
+    private static final String LOADER_ARG_UID = "uid";
+
+    private PowerStatsDataAdapter mPowerStatsDataAdapter;
+    private Runnable mPowerStatsRefresh = this::periodicPowerStatsRefresh;
+    private SharedPreferences mSharedPref;
+    private int mUid = Process.INVALID_UID;
+    private TextView mTitleView;
+    private TextView mUidView;
+    private ImageView mIconView;
+    private TextView mPackagesView;
+    private RecyclerView mPowerStatsDataView;
+    private View mLoadingView;
+    private View mEmptyView;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mSharedPref = getPreferences(Context.MODE_PRIVATE);
+
+        setContentView(R.layout.power_stats_viewer_layout);
+
+        View appCard = findViewById(R.id.app_card);
+        appCard.setOnClickListener((e) -> startAppPicker());
+
+        mTitleView = findViewById(android.R.id.title);
+        mUidView = findViewById(R.id.uid);
+        mIconView = findViewById(android.R.id.icon);
+        mPackagesView = findViewById(R.id.packages);
+
+        mPowerStatsDataView = findViewById(R.id.power_stats_data_view);
+        mPowerStatsDataView.setLayoutManager(new LinearLayoutManager(this));
+        mPowerStatsDataAdapter = new PowerStatsDataAdapter();
+        mPowerStatsDataView.setAdapter(mPowerStatsDataAdapter);
+
+        mLoadingView = findViewById(R.id.loading_view);
+        mEmptyView = findViewById(R.id.empty_view);
+
+        mUid = mSharedPref.getInt(PREF_SELECTED_UID, Process.INVALID_UID);
+        loadPowerStats();
+        if (mUid == Process.INVALID_UID) {
+            startAppPicker();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        periodicPowerStatsRefresh();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        getMainThreadHandler().removeCallbacks(mPowerStatsRefresh);
+    }
+
+    private void startAppPicker() {
+        registerForActivityResult(AppPickerActivity.CONTRACT, this::onApplicationSelected)
+                .launch(null);
+    }
+
+    private void onApplicationSelected(int uid) {
+        if (uid == -1) {
+            if (mUid == Process.INVALID_UID) {
+                finish();
+            }
+        } else {
+            mUid = uid;
+            mSharedPref.edit().putInt(PREF_SELECTED_UID, mUid).apply();
+            mLoadingView.setVisibility(View.VISIBLE);
+            loadPowerStats();
+        }
+    }
+
+    private void periodicPowerStatsRefresh() {
+        loadPowerStats();
+        getMainThreadHandler().postDelayed(mPowerStatsRefresh, POWER_STATS_REFRESH_RATE_MILLIS);
+    }
+
+    private void loadPowerStats() {
+        Bundle args = new Bundle();
+        args.putInt(LOADER_ARG_UID, mUid);
+        LoaderManager.getInstance(this).restartLoader(0, args, new PowerStatsDataLoaderCallbacks());
+    }
+
+    private static class PowerStatsDataLoader extends AsyncLoaderCompat<PowerStatsData> {
+        private final int mUid;
+        private final BatteryStatsHelper mBatteryStatsHelper;
+        private final UserManager mUserManager;
+
+        PowerStatsDataLoader(Context context, int uid) {
+            super(context);
+            mUid = uid;
+            mUserManager = context.getSystemService(UserManager.class);
+            mBatteryStatsHelper = new BatteryStatsHelper(context,
+                    false /* collectBatteryBroadcast */);
+            mBatteryStatsHelper.create((Bundle) null);
+            mBatteryStatsHelper.clearStats();
+        }
+
+        @Override
+        public PowerStatsData loadInBackground() {
+            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
+                    mUserManager.getUserProfiles());
+            return new PowerStatsData(getContext(), mBatteryStatsHelper, mUid);
+        }
+
+        @Override
+        protected void onDiscardResult(PowerStatsData result) {
+        }
+    }
+
+    private class PowerStatsDataLoaderCallbacks implements LoaderCallbacks<PowerStatsData> {
+        @NonNull
+        @Override
+        public Loader<PowerStatsData> onCreateLoader(int id, Bundle args) {
+            return new PowerStatsDataLoader(PowerStatsViewerActivity.this,
+                    args.getInt(LOADER_ARG_UID, Process.INVALID_UID));
+        }
+
+        @Override
+        public void onLoadFinished(@NonNull Loader<PowerStatsData> loader,
+                PowerStatsData powerStatsData) {
+
+            AppInfoHelper.AppInfo appInfo = powerStatsData.getAppInfo();
+            mTitleView.setText(appInfo.label);
+            mUidView.setText(String.format(Locale.getDefault(), "UID: %d", appInfo.uid));
+            mIconView.setImageDrawable(appInfo.iconInfo.loadIcon(getPackageManager()));
+
+            if (appInfo.packages != null) {
+                mPackagesView.setText(appInfo.packages);
+                mPackagesView.setVisibility(View.VISIBLE);
+            } else {
+                mPackagesView.setVisibility(View.GONE);
+            }
+
+            mPowerStatsDataAdapter.setEntries(powerStatsData.getEntries());
+
+            if (powerStatsData.getEntries().isEmpty()) {
+                mEmptyView.setVisibility(View.VISIBLE);
+                mPowerStatsDataView.setVisibility(View.GONE);
+            } else {
+                mEmptyView.setVisibility(View.GONE);
+                mPowerStatsDataView.setVisibility(View.VISIBLE);
+            }
+
+            mLoadingView.setVisibility(View.GONE);
+        }
+
+        @Override
+        public void onLoaderReset(@NonNull Loader<PowerStatsData> loader) {
+        }
+    }
+
+    private static class PowerStatsDataAdapter extends
+            RecyclerView.Adapter<PowerStatsDataAdapter.ViewHolder> {
+        public static class ViewHolder extends RecyclerView.ViewHolder {
+            public TextView titleTextView;
+            public TextView amountTextView;
+            public TextView percentTextView;
+
+            ViewHolder(View itemView) {
+                super(itemView);
+
+                titleTextView = itemView.findViewById(R.id.title);
+                amountTextView = itemView.findViewById(R.id.amount);
+                percentTextView = itemView.findViewById(R.id.percent);
+            }
+        }
+
+        private List<PowerStatsData.Entry> mEntries = Collections.emptyList();
+
+        public void setEntries(List<PowerStatsData.Entry> entries) {
+            mEntries = entries;
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getItemCount() {
+            return mEntries.size();
+        }
+
+        @NonNull
+        @Override
+        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
+            LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
+            View itemView = layoutInflater.inflate(R.layout.power_stats_entry_layout, parent,
+                    false);
+            return new ViewHolder(itemView);
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
+            PowerStatsData.Entry entry = mEntries.get(position);
+            switch (entry.entryType) {
+                case POWER:
+                    viewHolder.titleTextView.setText(entry.title);
+                    viewHolder.amountTextView.setText(
+                            String.format(Locale.getDefault(), "%.1f mAh", entry.value));
+                    break;
+                case DURATION:
+                    viewHolder.titleTextView.setText(entry.title);
+                    viewHolder.amountTextView.setText(
+                            String.format(Locale.getDefault(), "%,d ms", (long) entry.value));
+                    break;
+            }
+
+            double proportion = entry.total != 0 ? entry.value * 100 / entry.total : 0;
+            viewHolder.percentTextView.setText(String.format(Locale.getDefault(), "%.1f%%",
+                    proportion));
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 81da5c8..4c3b36f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -437,6 +437,9 @@
         <permission name="android.permission.MANAGE_DEBUGGING" />
         <!-- Permissions required for CTS test - TimeManagerTest -->
         <permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+        <!-- Permissions required for CTS test - android.server.biometrics -->
+        <permission name="android.permission.USE_BIOMETRIC" />
+        <permission name="android.permission.TEST_BIOMETRIC" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index d408ac3..3260849 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -73,6 +73,9 @@
         }
     }
 
+    /**
+     * @return the backing ColorSpace that this ParcelableColorSpace is wrapping.
+     */
     public @NonNull ColorSpace getColorSpace() {
         return mColorSpace;
     }
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
index 227eec2..bcef154 100644
--- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -1,6 +1,12 @@
 {
   "version": "1.0.0",
   "messages": {
+    "-1823823103": {
+      "message": "Add listener for types=%s listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
     "-1534364071": {
       "message": "onTransitionReady %s: %s",
       "level": "VERBOSE",
@@ -37,12 +43,6 @@
       "group": "WM_SHELL_TASK_ORG",
       "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
     },
-    "-242812822": {
-      "message": "Add listener for modes=%s listener=%s",
-      "level": "VERBOSE",
-      "group": "WM_SHELL_TASK_ORG",
-      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
-    },
     "-191422040": {
       "message": "Transition animations finished, notifying core %s",
       "level": "VERBOSE",
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 4f610efd..7ce65fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -16,9 +16,17 @@
 
 package com.android.wm.shell;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+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 android.annotation.IntDef;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration.WindowingMode;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -33,7 +41,6 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
@@ -42,6 +49,23 @@
  */
 public class ShellTaskOrganizer extends TaskOrganizer {
 
+    // Intentionally using negative numbers here so the positive numbers can be used
+    // for task id specific listeners that will be added later.
+    public static final int TASK_LISTENER_TYPE_UNDEFINED = -1;
+    public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2;
+    public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3;
+    public static final int TASK_LISTENER_TYPE_PIP = -4;
+    public static final int TASK_LISTENER_TYPE_SPLIT_SCREEN = -5;
+
+    @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = {
+            TASK_LISTENER_TYPE_UNDEFINED,
+            TASK_LISTENER_TYPE_FULLSCREEN,
+            TASK_LISTENER_TYPE_MULTI_WINDOW,
+            TASK_LISTENER_TYPE_PIP,
+            TASK_LISTENER_TYPE_SPLIT_SCREEN,
+    })
+    public @interface TaskListenerType {}
+
     private static final String TAG = "ShellTaskOrganizer";
 
     /**
@@ -54,7 +78,7 @@
         default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
     }
 
-    private final SparseArray<TaskListener> mListenerByWindowingMode = new SparseArray<>();
+    private final SparseArray<TaskListener> mTaskListenersByType = new SparseArray<>();
 
     // Keeps track of all the tasks reported to this organizer (changes in windowing mode will
     // require us to report to both old and new listeners)
@@ -73,29 +97,29 @@
             SyncTransactionQueue syncQueue, TransactionPool transactionPool,
             ShellExecutor mainExecutor, ShellExecutor animExecutor) {
         super(taskOrganizerController);
-        addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
+        addListener(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN);
         mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
         if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
     }
 
     /**
-     * Adds a listener for tasks in a specific windowing mode.
+     * Adds a listener for tasks with given types.
      */
-    public void addListener(TaskListener listener, int... windowingModes) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s",
-                Arrays.toString(windowingModes), listener);
-        for (int winMode : windowingModes) {
-            if (mListenerByWindowingMode.get(winMode) != null) {
-                throw new IllegalArgumentException("Listener for winMode=" + winMode
+    public void addListener(TaskListener listener, @TaskListenerType int... taskListenerTypes) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for types=%s listener=%s",
+                Arrays.toString(taskListenerTypes), listener);
+        for (int listenerType : taskListenerTypes) {
+            if (mTaskListenersByType.get(listenerType) != null) {
+                throw new IllegalArgumentException("Listener for listenerType=" + listenerType
                         + " already exists");
             }
-            mListenerByWindowingMode.put(winMode, listener);
+            mTaskListenersByType.put(listenerType, listener);
 
-            // Notify the listener of all existing tasks in that windowing mode
+            // Notify the listener of all existing tasks with the given type.
             for (int i = mTasks.size() - 1; i >= 0; i--) {
                 Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
-                int taskWinMode = data.first.configuration.windowConfiguration.getWindowingMode();
-                if (taskWinMode == winMode) {
+                final @TaskListenerType int taskListenerType = getTaskListenerType(data.first);
+                if (taskListenerType == listenerType) {
                     listener.onTaskAppeared(data.first, data.second);
                 }
             }
@@ -107,12 +131,12 @@
      */
     public void removeListener(TaskListener listener) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
-        final int index = mListenerByWindowingMode.indexOfValue(listener);
+        final int index = mTaskListenersByType.indexOfValue(listener);
         if (index == -1) {
             Log.w(TAG, "No registered listener found");
             return;
         }
-        mListenerByWindowingMode.removeAt(index);
+        mTaskListenersByType.removeAt(index);
     }
 
     @Override
@@ -120,7 +144,7 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
                 taskInfo.taskId);
         mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
-        final TaskListener listener = mListenerByWindowingMode.get(getWindowingMode(taskInfo));
+        final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
         if (listener != null) {
             listener.onTaskAppeared(taskInfo, leash);
         }
@@ -131,23 +155,23 @@
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
                 taskInfo.taskId);
         final Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
-        final int winMode = getWindowingMode(taskInfo);
-        final int prevWinMode = getWindowingMode(data.first);
+        final @TaskListenerType int listenerType = getTaskListenerType(taskInfo);
+        final @TaskListenerType int prevListenerType = getTaskListenerType(data.first);
         mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second));
-        if (prevWinMode != -1 && prevWinMode != winMode) {
-            // TODO: We currently send vanished/appeared as the task moves between win modes, but
+        if (prevListenerType != listenerType) {
+            // TODO: We currently send vanished/appeared as the task moves between types, but
             //       we should consider adding a different mode-changed callback
-            TaskListener listener = mListenerByWindowingMode.get(prevWinMode);
+            TaskListener listener = mTaskListenersByType.get(prevListenerType);
             if (listener != null) {
                 listener.onTaskVanished(taskInfo);
             }
-            listener = mListenerByWindowingMode.get(winMode);
+            listener = mTaskListenersByType.get(listenerType);
             if (listener != null) {
                 SurfaceControl leash = data.second;
                 listener.onTaskAppeared(taskInfo, leash);
             }
         } else {
-            final TaskListener listener = mListenerByWindowingMode.get(winMode);
+            final TaskListener listener = mTaskListenersByType.get(listenerType);
             if (listener != null) {
                 listener.onTaskInfoChanged(taskInfo);
             }
@@ -158,7 +182,7 @@
     public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
                 taskInfo.taskId);
-        final TaskListener listener = mListenerByWindowingMode.get(getWindowingMode(taskInfo));
+        final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo));
         if (listener != null) {
             listener.onBackPressedOnTaskRoot(taskInfo);
         }
@@ -168,15 +192,38 @@
     public void onTaskVanished(RunningTaskInfo taskInfo) {
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
                 taskInfo.taskId);
-        final int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
+        final @TaskListenerType int prevListenerType =
+                getTaskListenerType(mTasks.get(taskInfo.taskId).first);
         mTasks.remove(taskInfo.taskId);
-        final TaskListener listener = mListenerByWindowingMode.get(prevWinMode);
+        final TaskListener listener = mTaskListenersByType.get(prevListenerType);
         if (listener != null) {
             listener.onTaskVanished(taskInfo);
         }
     }
 
-    private int getWindowingMode(RunningTaskInfo taskInfo) {
+    @TaskListenerType
+    private static int getTaskListenerType(RunningTaskInfo runningTaskInfo) {
+        // Right now it's N:1 mapping but in the future different task listerners
+        // may be triggered by one windowing mode depending on task parameters.
+        switch (getWindowingMode(runningTaskInfo)) {
+            case WINDOWING_MODE_FULLSCREEN:
+                return TASK_LISTENER_TYPE_FULLSCREEN;
+            case WINDOWING_MODE_MULTI_WINDOW:
+                return TASK_LISTENER_TYPE_MULTI_WINDOW;
+            case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+            case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
+                return TASK_LISTENER_TYPE_SPLIT_SCREEN;
+            case WINDOWING_MODE_PINNED:
+                return TASK_LISTENER_TYPE_PIP;
+            case WINDOWING_MODE_FREEFORM:
+            case WINDOWING_MODE_UNDEFINED:
+            default:
+                return TASK_LISTENER_TYPE_UNDEFINED;
+        }
+    }
+
+    @WindowingMode
+    private static int getWindowingMode(RunningTaskInfo taskInfo) {
         return taskInfo.configuration.windowConfiguration.getWindowingMode();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
new file mode 100644
index 0000000..10e5c3d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -0,0 +1,49 @@
+/*
+ * 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.pip;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+
+import java.io.PrintWriter;
+
+/**
+ * Singleton source of truth for the current state of PIP bounds.
+ */
+public final class PipBoundsState {
+    private static final String TAG = PipBoundsState.class.getSimpleName();
+
+    private final @NonNull Rect mBounds = new Rect();
+
+    void setBounds(@NonNull Rect bounds) {
+        mBounds.set(bounds);
+    }
+
+    @NonNull
+    public Rect getBounds() {
+        return new Rect(mBounds);
+    }
+
+    /**
+     * Dumps internal state.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mBounds=" + mBounds);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 05877d4..3485c7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
@@ -133,11 +134,11 @@
 
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
+    private final PipBoundsState mPipBoundsState;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipAnimationController mPipAnimationController;
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
-    private final Rect mLastReportedBounds = new Rect();
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
@@ -261,7 +262,8 @@
      */
     private boolean mShouldIgnoreEnteringPipTransition;
 
-    public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
+    public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
+            @NonNull PipBoundsHandler boundsHandler,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional,
             @NonNull DisplayController displayController,
@@ -269,6 +271,7 @@
             @NonNull ShellTaskOrganizer shellTaskOrganizer) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
+        mPipBoundsState = pipBoundsState;
         mPipBoundsHandler = boundsHandler;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
@@ -282,7 +285,7 @@
         if (!PipUtils.hasSystemFeature(context)) {
             Log.w(TAG, "Device not support PIP feature");
         } else {
-            mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
+            mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_PIP);
             displayController.addDisplayWindowListener(this);
         }
     }
@@ -291,17 +294,13 @@
         return mUpdateHandler;
     }
 
-    public Rect getLastReportedBounds() {
-        return new Rect(mLastReportedBounds);
-    }
-
     public Rect getCurrentOrAnimatingBounds() {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
         if (animator != null && animator.isRunning()) {
             return new Rect(animator.getDestinationBounds());
         }
-        return getLastReportedBounds();
+        return mPipBoundsState.getBounds();
     }
 
     public boolean isInPip() {
@@ -346,7 +345,7 @@
      * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
      */
     public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
-        mLastReportedBounds.set(destinationBounds);
+        mPipBoundsState.setBounds(destinationBounds);
     }
 
     /**
@@ -393,7 +392,7 @@
             final SurfaceControl.Transaction tx =
                     mSurfaceControlTransactionFactory.getTransaction();
             mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
-                    mLastReportedBounds);
+                    mPipBoundsState.getBounds());
             tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
             // We set to fullscreen here for now, but later it will be set to UNDEFINED for
             // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
@@ -407,9 +406,9 @@
                 @Override
                 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                     t.apply();
-                    scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
-                            getValidSourceHintRect(mTaskInfo, destinationBounds), direction,
-                            animationDurationMs, null /* updateBoundsCallback */);
+                    scheduleAnimateResizePip(mPipBoundsState.getBounds(),
+                            destinationBounds, getValidSourceHintRect(mTaskInfo, destinationBounds),
+                            direction, animationDurationMs, null /* updateBoundsCallback */);
                     mState = State.EXITING_PIP;
                 }
             });
@@ -440,7 +439,7 @@
 
         // removePipImmediately is expected when the following animation finishes.
         mUpdateHandler.post(() -> mPipAnimationController
-                .getAnimator(mLeash, mLastReportedBounds, 1f, 0f)
+                .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f)
                 .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK)
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration)
@@ -479,7 +478,7 @@
         if (mShouldIgnoreEnteringPipTransition) {
             // Animation has been finished together with Recents, directly apply the sync
             // transaction to PiP here.
-            applyEnterPipSyncTransaction(mLastReportedBounds, () -> {
+            applyEnterPipSyncTransaction(mPipBoundsState.getBounds(), () -> {
                 mState = State.ENTERED_PIP;
             });
             mShouldIgnoreEnteringPipTransition = false;
@@ -571,7 +570,7 @@
 
     private void sendOnPipTransitionStarted(
             @PipAnimationController.TransitionDirection int direction) {
-        final Rect pipBounds = new Rect(mLastReportedBounds);
+        final Rect pipBounds = mPipBoundsState.getBounds();
         runOnMainHandler(() -> {
             for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -700,7 +699,7 @@
         }
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                 info.topActivity, getAspectRatioOrDefault(newParams),
-                mLastReportedBounds, getMinimalSize(info.topActivityInfo),
+                mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
                 true /* userCurrentMinEdgeSize */);
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
@@ -758,7 +757,7 @@
                     sendOnPipTransitionCancelled(direction);
                     sendOnPipTransitionFinished(direction);
                 }
-                mLastReportedBounds.set(destinationBoundsOut);
+                mPipBoundsState.setBounds(destinationBoundsOut);
 
                 // Create a reset surface transaction for the new bounds and update the window
                 // container transaction
@@ -773,8 +772,8 @@
                         destinationBoundsOut.set(animator.getDestinationBounds());
                     }
                 } else {
-                    if (!mLastReportedBounds.isEmpty()) {
-                        destinationBoundsOut.set(mLastReportedBounds);
+                    if (!mPipBoundsState.getBounds().isEmpty()) {
+                        destinationBoundsOut.set(mPipBoundsState.getBounds());
                     }
                 }
             }
@@ -826,7 +825,7 @@
             Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
             return;
         }
-        scheduleAnimateResizePip(mLastReportedBounds, toBounds, null /* sourceHintRect */,
+        scheduleAnimateResizePip(mPipBoundsState.getBounds(), toBounds, null /* sourceHintRect */,
                 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
     }
 
@@ -962,7 +961,7 @@
             Log.w(TAG, "Abort animation, invalid leash");
             return;
         }
-        mLastReportedBounds.set(destinationBounds);
+        mPipBoundsState.setBounds(destinationBounds);
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
@@ -998,7 +997,7 @@
             throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
                     + "directly");
         }
-        mLastReportedBounds.set(destinationBounds);
+        mPipBoundsState.setBounds(destinationBounds);
         if (direction == TRANSITION_DIRECTION_REMOVE_STACK) {
             removePipImmediately();
             return;
@@ -1140,7 +1139,6 @@
         pw.println(innerPrefix + "mState=" + mState);
         pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
         pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
-        pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
         pw.println(innerPrefix + "mInitialState:");
         for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
             pw.println(innerPrefix + "  binder=" + e.getKey()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index fddd547..18b6922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -15,6 +15,7 @@
  */
 package com.android.wm.shell.pip.phone;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -28,6 +29,7 @@
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
 import com.android.wm.shell.R;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
@@ -50,6 +52,7 @@
 
     private Context mContext;
     private Handler mHandler;
+    private final @NonNull PipBoundsState mPipBoundsState;
     private PipMotionHelper mMotionHelper;
     private PipTaskOrganizer mTaskOrganizer;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -62,12 +65,14 @@
     private final Rect mExpandedMovementBounds = new Rect();
     private Rect mTmpBounds = new Rect();
 
-    public PipAccessibilityInteractionConnection(Context context, PipMotionHelper motionHelper,
+    public PipAccessibilityInteractionConnection(Context context,
+            @NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
             PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
             AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
             Handler handler) {
         mContext = context;
         mHandler = handler;
+        mPipBoundsState = pipBoundsState;
         mMotionHelper = motionHelper;
         mTaskOrganizer = taskOrganizer;
         mSnapAlgorithm = snapAlgorithm;
@@ -148,7 +153,7 @@
 
     private void setToExpandedBounds() {
         float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
-                new Rect(mTaskOrganizer.getLastReportedBounds()), mNormalMovementBounds);
+                mPipBoundsState.getBounds(), mNormalMovementBounds);
         mSnapAlgorithm.applySnapFraction(mExpandedBounds, mExpandedMovementBounds,
                 savedSnapFraction);
         mTaskOrganizer.scheduleFinishResizePip(mExpandedBounds, (Rect bounds) -> {
@@ -159,7 +164,7 @@
 
     private void setToNormalBounds() {
         float savedSnapFraction = mSnapAlgorithm.getSnapFraction(
-                new Rect(mTaskOrganizer.getLastReportedBounds()), mExpandedMovementBounds);
+                mPipBoundsState.getBounds(), mExpandedMovementBounds);
         mSnapAlgorithm.applySnapFraction(mNormalBounds, mNormalMovementBounds, savedSnapFraction);
         mTaskOrganizer.scheduleFinishResizePip(mNormalBounds, (Rect bounds) -> {
             mMotionHelper.synchronizePinnedStackBounds();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 41c0a88..d191c98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -20,6 +20,7 @@
 
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
@@ -44,6 +45,7 @@
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
 import java.io.PrintWriter;
@@ -66,6 +68,7 @@
     private DisplayController mDisplayController;
     private PipAppOpsListener mAppOpsListener;
     private PipBoundsHandler mPipBoundsHandler;
+    private @NonNull PipBoundsState mPipBoundsState;
     private PipMediaController mMediaController;
     private PipTouchHandler mTouchHandler;
     private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
@@ -98,7 +101,7 @@
             // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the
             // movement bounds
             mTouchHandler.adjustBoundsForRotation(mTmpNormalBounds,
-                    mPipTaskOrganizer.getLastReportedBounds(), mTmpInsetBounds);
+                    mPipBoundsState.getBounds(), mTmpInsetBounds);
 
             // The bounds are being applied to a specific snap fraction, so reset any known offsets
             // for the previous orientation before updating the movement bounds.
@@ -197,6 +200,7 @@
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsHandler pipBoundsHandler,
+            @NonNull PipBoundsState pipBoundsState,
             PipMediaController pipMediaController,
             PipMenuActivityController pipMenuActivityController,
             PipTaskOrganizer pipTaskOrganizer,
@@ -207,7 +211,7 @@
 
         if (PipUtils.hasSystemFeature(mContext)) {
             initController(context, displayController, pipAppOpsListener, pipBoundsHandler,
-                    pipMediaController, pipMenuActivityController, pipTaskOrganizer,
+                    pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer,
                     pipTouchHandler, windowManagerShellWrapper);
         } else {
             Log.w(TAG, "Device not support PIP feature");
@@ -218,6 +222,7 @@
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsHandler pipBoundsHandler,
+            @NonNull PipBoundsState pipBoundsState,
             PipMediaController pipMediaController,
             PipMenuActivityController pipMenuActivityController,
             PipTaskOrganizer pipTaskOrganizer,
@@ -233,6 +238,7 @@
         mWindowManagerShellWrapper = windowManagerShellWrapper;
         mDisplayController = displayController;
         mPipBoundsHandler = pipBoundsHandler;
+        mPipBoundsState = pipBoundsState;
         mPipTaskOrganizer = pipTaskOrganizer;
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mMediaController = pipMediaController;
@@ -361,7 +367,7 @@
         final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
         if (changed) {
             mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
-            updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
+            updateMovementBounds(mPipBoundsState.getBounds(),
                     false /* fromRotation */, false /* fromImeAdjustment */,
                     true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
         }
@@ -457,5 +463,6 @@
         mTouchHandler.dump(pw, innerPrefix);
         mPipBoundsHandler.dump(pw, innerPrefix);
         mPipTaskOrganizer.dump(pw, innerPrefix);
+        mPipBoundsState.dump(pw, innerPrefix);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index fe1d44c7..b5fa030 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -37,6 +37,7 @@
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 
@@ -66,6 +67,7 @@
 
     private final Context mContext;
     private final PipTaskOrganizer mPipTaskOrganizer;
+    private final @NonNull PipBoundsState mPipBoundsState;
 
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
@@ -178,11 +180,12 @@
         public void onPipTransitionCanceled(ComponentName activity, int direction) {}
     };
 
-    public PipMotionHelper(Context context, PipTaskOrganizer pipTaskOrganizer,
-            PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm,
-            FloatingContentCoordinator floatingContentCoordinator) {
+    public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState,
+            PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
+            PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) {
         mContext = context;
         mPipTaskOrganizer = pipTaskOrganizer;
+        mPipBoundsState = pipBoundsState;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -220,7 +223,7 @@
      */
     void synchronizePinnedStackBounds() {
         cancelAnimations();
-        mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
+        mBounds.set(mPipBoundsState.getBounds());
         mTemporaryBounds.setEmpty();
 
         if (mPipTaskOrganizer.isInPip()) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 07beb43..a2233e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -22,6 +22,7 @@
 import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
 import static com.android.wm.shell.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
 
+import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.content.ComponentName;
 import android.content.Context;
@@ -48,6 +49,7 @@
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
 
@@ -70,6 +72,7 @@
     private final boolean mEnableResize;
     private final Context mContext;
     private final PipBoundsHandler mPipBoundsHandler;
+    private final @NonNull PipBoundsState mPipBoundsState;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipDismissTargetHandler mPipDismissTargetHandler;
 
@@ -161,6 +164,7 @@
     public PipTouchHandler(Context context,
             PipMenuActivityController menuController,
             PipBoundsHandler pipBoundsHandler,
+            @NonNull PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger) {
@@ -168,11 +172,12 @@
         mContext = context;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsHandler = pipBoundsHandler;
+        mPipBoundsState = pipBoundsState;
         mMenuController = menuController;
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
-        mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController,
-                mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
+        mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer,
+                mMenuController, mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         pipTaskOrganizer, this::getMovementBounds,
@@ -189,8 +194,8 @@
         reloadResources();
 
         mFloatingContentCoordinator = floatingContentCoordinator;
-        mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
-                pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
+        mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
+                mMotionHelper, pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
                 this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler);
 
         mPipUiEventLogger = pipUiEventLogger;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
index 30bc43b..8660702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java
@@ -23,8 +23,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN;
+
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
@@ -56,17 +57,16 @@
                     ShellTaskOrganizer shellTaskOrganizer) {
         mSplitScreenController = splitScreenController;
         mTaskOrganizer = shellTaskOrganizer;
-        mTaskOrganizer.addListener(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_SPLIT_SCREEN);
     }
 
     void init() throws RemoteException {
         synchronized (this) {
             try {
                 mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
-                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+                        WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
                 mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
-                        WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             } catch (Exception e) {
                 // teardown to prevent callbacks
                 mTaskOrganizer.removeListener(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 6ce4d37..f01fc51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -19,6 +19,9 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP;
+
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -102,10 +105,10 @@
     }
 
     @Test
-    public void testOneListenerPerMode() {
-        mOrganizer.addListener(new TrackingTaskListener(), WINDOWING_MODE_MULTI_WINDOW);
+    public void testOneListenerPerType() {
+        mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
         try {
-            mOrganizer.addListener(new TrackingTaskListener(), WINDOWING_MODE_MULTI_WINDOW);
+            mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW);
             fail("Expected exception due to already registered listener");
         } catch (Exception e) {
             // Expected failure
@@ -116,7 +119,7 @@
     public void testAppearedVanished() {
         RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(listener.appeared.contains(taskInfo));
 
@@ -130,7 +133,7 @@
         mOrganizer.onTaskAppeared(taskInfo, null);
 
         TrackingTaskListener listener = new TrackingTaskListener();
-        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW);
         assertTrue(listener.appeared.contains(taskInfo));
     }
 
@@ -139,8 +142,8 @@
         RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
         TrackingTaskListener mwListener = new TrackingTaskListener();
         TrackingTaskListener pipListener = new TrackingTaskListener();
-        mOrganizer.addListener(mwListener, WINDOWING_MODE_MULTI_WINDOW);
-        mOrganizer.addListener(pipListener, WINDOWING_MODE_PINNED);
+        mOrganizer.addListener(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW);
+        mOrganizer.addListener(pipListener, TASK_LISTENER_TYPE_PIP);
         mOrganizer.onTaskAppeared(taskInfo, null);
         assertTrue(mwListener.appeared.contains(taskInfo));
         assertTrue(pipListener.appeared.isEmpty());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 7d51886..255e749 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
 
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
index f514b0b..d9e3148 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip;
+package com.android.wm.shell.pip;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index d305c64..2b987e9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
@@ -34,6 +34,7 @@
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTestCase;
 import com.android.wm.shell.pip.phone.PipAppOpsListener;
@@ -67,10 +68,12 @@
     @Mock private PipTaskOrganizer mMockPipTaskOrganizer;
     @Mock private PipTouchHandler mMockPipTouchHandler;
     @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
+    private PipBoundsState mPipBoundsState;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
+        mPipBoundsState = new PipBoundsState();
 
         mSpiedContext = spy(mContext);
 
@@ -78,9 +81,9 @@
         when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
 
         mPipController = new PipController(mSpiedContext, mMockdDisplayController,
-                mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipMediaController,
-                mMockPipMenuActivityController, mMockPipTaskOrganizer, mMockPipTouchHandler,
-                mMockWindowManagerShellWrapper);
+                mMockPipAppOpsListener, mMockPipBoundsHandler, mPipBoundsState,
+                mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer,
+                mMockPipTouchHandler, mMockWindowManagerShellWrapper);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
index 663169f..37b93bcd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 
@@ -35,6 +35,7 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTestCase;
@@ -66,19 +67,21 @@
     @Mock private PipUiEventLogger mMockPipUiEventLogger;
     @Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
+    private PipBoundsState mPipBoundsState;
 
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
+        mPipBoundsState = new PipBoundsState();
 
         mSpiedContext = spy(mContext);
 
         when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
         when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
 
-        mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mMockPipBoundsHandler,
-                mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController,
-                mMockPipUiEventLogger, mMockShellTaskOrganizer));
+        mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mPipBoundsState,
+                mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen,
+                mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index c96cb20..4713142 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
@@ -33,6 +33,7 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSnapAlgorithm;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipTestCase;
@@ -74,6 +75,7 @@
     @Mock
     private PipUiEventLogger mPipUiEventLogger;
 
+    private PipBoundsState mPipBoundsState;
     private PipBoundsHandler mPipBoundsHandler;
     private PipSnapAlgorithm mPipSnapAlgorithm;
     private PipMotionHelper mMotionHelper;
@@ -90,11 +92,12 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mPipBoundsState = new PipBoundsState();
         mPipBoundsHandler = new PipBoundsHandler(mContext);
         mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
         mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
         mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
-                mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator,
+                mPipBoundsHandler, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator,
                 mPipUiEventLogger);
         mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
         mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
index 2702130..40667f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone;
+package com.android.wm.shell.pip.phone;
 
 import static android.view.MotionEvent.ACTION_BUTTON_PRESS;
 import static android.view.MotionEvent.ACTION_DOWN;
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index b802908..5f6b53a 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -21,7 +21,6 @@
 #include <log/log.h>
 
 #include <minikin/MeasuredText.h>
-#include <minikin/Measurement.h>
 #include "Paint.h"
 #include "SkPathMeasure.h"
 #include "Typeface.h"
@@ -70,18 +69,6 @@
     }
 }
 
-void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
-                             const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out) {
-    minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
-
-    const minikin::U16StringPiece textBuf(buf, bufSize);
-    const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit();
-    const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
-
-    minikin::getBounds(textBuf, minikin::Range(0, textBuf.size()), bidiFlags, minikinPaint,
-        startHyphen, endHyphen, out);
-}
-
 float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
                                 const Typeface* typeface, const uint16_t* buf, size_t start,
                                 size_t count, size_t bufSize, float* advances) {
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index a15803a..7c3f0d8 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -48,9 +48,6 @@
                                                 size_t contextStart, size_t contextCount,
                                                 minikin::MeasuredText* mt);
 
-    static void getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
-                          const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out);
-
     static float measureText(const Paint* paint, minikin::Bidi bidiFlags,
                                          const Typeface* typeface, const uint16_t* buf,
                                          size_t start, size_t count, size_t bufSize,
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 3c86b28..89ff9b2 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -339,13 +339,18 @@
     }
 
     static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
-            const Paint& paint, const Typeface* typeface, jint bidiFlagsInt) {
+            const Paint& paint, const Typeface* typeface, jint bidiFlags) {
         SkRect  r;
         SkIRect ir;
 
+        minikin::Layout layout = MinikinUtils::doLayout(&paint,
+                static_cast<minikin::Bidi>(bidiFlags), typeface,
+                text, count,  // text buffer
+                0, count,  // draw range
+                0, count,  // context range
+                nullptr);
         minikin::MinikinRect rect;
-        minikin::Bidi bidiFlags = static_cast<minikin::Bidi>(bidiFlagsInt);
-        MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, count, &rect);
+        layout.getBounds(&rect);
         r.fLeft = rect.mLeft;
         r.fTop = rect.mTop;
         r.fRight = rect.mRight;
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 5ae9dd2..42cf53b 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -106,11 +106,12 @@
     boolean isProviderEnabledForUser(String provider, int userId);
     boolean isLocationEnabledForUser(int userId);
     void setLocationEnabledForUser(boolean enabled, int userId);
+
     void addTestProvider(String name, in ProviderProperties properties, String packageName, String attributionTag);
     void removeTestProvider(String provider, String packageName, String attributionTag);
     void setTestProviderLocation(String provider, in Location location, String packageName, String attributionTag);
     void setTestProviderEnabled(String provider, boolean enabled, String packageName, String attributionTag);
-    List<LocationRequest> getTestProviderCurrentRequests(String provider);
+
     LocationTime getGnssTimeMillis();
 
     void sendExtraCommand(String provider, String command, inout Bundle extras);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f87988c..30a4ada 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1815,22 +1815,6 @@
     public void clearTestProviderStatus(@NonNull String provider) {}
 
     /**
-     * Get the last list of {@link LocationRequest}s sent to the provider.
-     *
-     * @hide
-     */
-    @TestApi
-    @NonNull
-    public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
-        Preconditions.checkArgument(providerName != null, "invalid null provider");
-        try {
-            return mService.getTestProviderCurrentRequests(providerName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Sets a proximity alert for the location given by the position (latitude, longitude) and the
      * given radius.
      *
diff --git a/media/OWNERS b/media/OWNERS
index 36df3a0..0fc781c 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -15,6 +15,8 @@
 klhyun@google.com
 lajos@google.com
 marcone@google.com
+nchalko@google.com
 philburk@google.com
+quxiangfang@google.com
 sungsoo@google.com
 wonsik@google.com
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 0747ab13..ae97a71 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2711,12 +2711,12 @@
             }
         };
 
-        private final Pattern zeroPattern = new Pattern(0, 0);
+        private static final Pattern ZERO_PATTERN = new Pattern(0, 0);
 
         /**
          * The pattern applicable to the protected data in each subsample.
          */
-        private Pattern pattern;
+        private Pattern mPattern = ZERO_PATTERN;
 
         /**
          * Set the subsample count, clear/encrypted sizes, key, IV and mode fields of
@@ -2735,22 +2735,30 @@
             key = newKey;
             iv = newIV;
             mode = newMode;
-            pattern = zeroPattern;
+            mPattern = ZERO_PATTERN;
+        }
+
+        /**
+         * Returns the {@link Pattern encryption pattern}.
+         */
+        public @NonNull Pattern getPattern() {
+            return new Pattern(mPattern.getEncryptBlocks(), mPattern.getSkipBlocks());
         }
 
         /**
          * Set the encryption pattern on a {@link MediaCodec.CryptoInfo} instance.
-         * See {@link MediaCodec.CryptoInfo.Pattern}.
+         * See {@link Pattern}.
          */
         public void setPattern(Pattern newPattern) {
             if (newPattern == null) {
-                newPattern = zeroPattern;
+                newPattern = ZERO_PATTERN;
             }
-            pattern = newPattern;
+            setPattern(newPattern.getEncryptBlocks(), newPattern.getSkipBlocks());
         }
 
+        // Accessed from android_media_MediaExtractor.cpp.
         private void setPattern(int blocksToEncrypt, int blocksToSkip) {
-            pattern = new Pattern(blocksToEncrypt, blocksToSkip);
+            mPattern = new Pattern(blocksToEncrypt, blocksToSkip);
         }
 
         @Override
@@ -2772,9 +2780,9 @@
             builder.append(", encrypted ");
             builder.append(Arrays.toString(numBytesOfEncryptedData));
             builder.append(", pattern (encrypt: ");
-            builder.append(pattern.mEncryptBlocks);
+            builder.append(mPattern.mEncryptBlocks);
             builder.append(", skip: ");
-            builder.append(pattern.mSkipBlocks);
+            builder.append(mPattern.mSkipBlocks);
             builder.append(")");
             return builder.toString();
         }
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index dbf4ad0..78eeca1 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -427,7 +427,7 @@
 
     private MediaMetadata(Parcel in) {
         mBundle = in.readBundle();
-        mBitmapDimensionLimit = Math.max(in.readInt(), 0);
+        mBitmapDimensionLimit = Math.max(in.readInt(), 1);
     }
 
     /**
@@ -518,17 +518,18 @@
 
     /**
      * Gets the width/height limit (in pixels) for the bitmaps when this metadata was created.
-     * This method returns a positive value or zero.
+     * This method always returns a positive value.
      * <p>
-     * If it returns a positive value, then all the bitmaps in this metadata has width/height
+     * If it returns {@link Integer#MAX_VALUE}, then no scaling down was applied to the bitmaps
+     * when this metadata was created.
+     * <p>
+     * If it returns another positive value, then all the bitmaps in this metadata has width/height
      * not greater than this limit. Bitmaps may have been scaled down according to the limit.
      * <p>
-     * If it returns zero, then no scaling down was applied to the bitmaps when this metadata
-     * was created.
      *
      * @see Builder#setBitmapDimensionLimit(int)
      */
-    public @IntRange(from = 0) int getBitmapDimensionLimit() {
+    public @IntRange(from = 1) int getBitmapDimensionLimit() {
         return mBitmapDimensionLimit;
     }
 
@@ -738,7 +739,7 @@
      */
     public static final class Builder {
         private final Bundle mBundle;
-        private int mBitmapDimensionLimit;
+        private int mBitmapDimensionLimit = Integer.MAX_VALUE;
 
         /**
          * Create an empty Builder. Any field that should be included in the
@@ -925,14 +926,21 @@
          * Bitmaps will be replaced with scaled down copies if their width (or height) is
          * larger than {@code bitmapDimensionLimit}.
          * <p>
-         * In order to unset the limit, pass zero as {@code bitmapDimensionLimit}.
+         * In order to unset the limit, pass {@link Integer#MAX_VALUE} as
+         * {@code bitmapDimensionLimit}.
          *
          * @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps
-         *                             contained in the metadata. Pass {@code 0} to unset the limit.
+         *                             contained in the metadata. Non-positive values are ignored.
+         *                             Pass {@link Integer#MAX_VALUE} to unset the limit.
          */
         @NonNull
-        public Builder setBitmapDimensionLimit(int bitmapDimensionLimit) {
-            mBitmapDimensionLimit = Math.max(bitmapDimensionLimit, 0);
+        public Builder setBitmapDimensionLimit(@IntRange(from = 1) int bitmapDimensionLimit) {
+            if (bitmapDimensionLimit > 0) {
+                mBitmapDimensionLimit = bitmapDimensionLimit;
+            } else {
+                Log.w(TAG, "setBitmapDimensionLimit(): Ignoring non-positive bitmapDimensionLimit: "
+                        + bitmapDimensionLimit);
+            }
             return this;
         }
 
@@ -942,7 +950,7 @@
          * @return The new MediaMetadata instance
          */
         public MediaMetadata build() {
-            if (mBitmapDimensionLimit > 0) {
+            if (mBitmapDimensionLimit != Integer.MAX_VALUE) {
                 for (String key : mBundle.keySet()) {
                     Object value = mBundle.get(key);
                     if (value instanceof Bitmap) {
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cf03b06..835a709 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -16,10 +16,13 @@
 
 package android.media;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -1331,7 +1334,6 @@
      * @see MediaFormat#COLOR_RANGE_FULL
      */
     public static final int METADATA_KEY_COLOR_RANGE    = 37;
-    // Add more here...
 
     /**
      * This key retrieves the sample rate in Hz, if available.
@@ -1344,4 +1346,13 @@
      * This is a signed 32-bit integer formatted as a string in base 10.
      */
     public static final int METADATA_KEY_BITS_PER_SAMPLE = 39;
+
+    /**
+     * This key retrieves the video codec mimetype if available.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40;
+
+    // Add more here...
 }
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 2b3f420c..4d87fb3 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -45,6 +45,9 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Display;
+import android.view.DisplayAddress;
+
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1737,7 +1740,9 @@
          */
         private static final int DEFAULT_PLAYBACK_VOLUME = DEFAULT_PLAYBACK_MAX_VOLUME;
 
-        RouteInfo(RouteCategory category) {
+        /** @hide */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public RouteInfo(RouteCategory category) {
             mCategory = category;
             mDeviceType = DEVICE_TYPE_UNKNOWN;
         }
@@ -2078,7 +2083,9 @@
             return mPresentationDisplay;
         }
 
-        boolean updatePresentationDisplay() {
+        /** @hide */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public boolean updatePresentationDisplay() {
             Display display = choosePresentationDisplay();
             if (mPresentationDisplay != display) {
                 mPresentationDisplay = display;
@@ -2088,41 +2095,81 @@
         }
 
         private Display choosePresentationDisplay() {
-            if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
-                Display[] displays = sStatic.getAllPresentationDisplays();
+            if ((getSupportedTypes() & ROUTE_TYPE_LIVE_VIDEO) == 0) {
+                return null;
+            }
+            final Display[] displays = getAllPresentationDisplays();
+            if (displays == null || displays.length == 0) {
+                return null;
+            }
 
-                // Ensure that the specified display is valid for presentations.
-                // This check will normally disallow the default display unless it was
-                // configured as a presentation display for some reason.
-                if (mPresentationDisplayId >= 0) {
-                    for (Display display : displays) {
-                        if (display.getDisplayId() == mPresentationDisplayId) {
-                            return display;
-                        }
+            // Ensure that the specified display is valid for presentations.
+            // This check will normally disallow the default display unless it was
+            // configured as a presentation display for some reason.
+            if (mPresentationDisplayId >= 0) {
+                for (Display display : displays) {
+                    if (display.getDisplayId() == mPresentationDisplayId) {
+                        return display;
                     }
-                    return null;
                 }
+                return null;
+            }
 
-                // Find the indicated Wifi display by its address.
-                if (mDeviceAddress != null) {
-                    for (Display display : displays) {
-                        if (display.getType() == Display.TYPE_WIFI
-                                && mDeviceAddress.equals(display.getAddress())) {
-                            return display;
-                        }
+            // Find the indicated Wifi display by its address.
+            if (getDeviceAddress() != null) {
+                for (Display display : displays) {
+                    if (display.getType() == Display.TYPE_WIFI
+                            && displayAddressEquals(display)) {
+                        return display;
                     }
-                    return null;
-                }
-
-                // For the default route, choose the first presentation display from the list.
-                if (this == sStatic.mDefaultAudioVideo && displays.length > 0) {
-                    return displays[0];
                 }
             }
+
+            // Returns the first hard-wired display.
+            for (Display display : displays) {
+                if (display.getType() == Display.TYPE_EXTERNAL) {
+                    return display;
+                }
+            }
+
+            // Returns the first non-default built-in display.
+            for (Display display : displays) {
+                if (display.getType() == Display.TYPE_INTERNAL) {
+                    return display;
+                }
+            }
+
+            // For the default route, choose the first presentation display from the list.
+            if (this == getDefaultAudioVideo()) {
+                return displays[0];
+            }
             return null;
         }
 
         /** @hide */
+        @VisibleForTesting
+        public Display[] getAllPresentationDisplays() {
+            return sStatic.getAllPresentationDisplays();
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public RouteInfo getDefaultAudioVideo() {
+            return sStatic.mDefaultAudioVideo;
+        }
+
+        private boolean displayAddressEquals(Display display) {
+            final DisplayAddress displayAddress = display.getAddress();
+            // mDeviceAddress recorded mac address. If displayAddress is not a kind of Network,
+            // return false early.
+            if (!(displayAddress instanceof DisplayAddress.Network)) {
+                return false;
+            }
+            final DisplayAddress.Network networkAddress = (DisplayAddress.Network) displayAddress;
+            return getDeviceAddress().equals(networkAddress.toString());
+        }
+
+        /** @hide */
         @UnsupportedAppUsage
         public String getDeviceAddress() {
             return mDeviceAddress;
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl
new file mode 100644
index 0000000..9e71afa
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionManager.aidl
@@ -0,0 +1,14 @@
+package android.media.musicrecognition;
+
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.IBinder;
+
+/**
+ * Used by {@link MusicRecognitionManager} to tell system server to begin open an audio stream to
+ * the designated lookup service.
+ *
+ * @hide
+ */
+interface IMusicRecognitionManager {
+    void beginRecognition(in RecognitionRequest recognitionRequest, in IBinder callback);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl
new file mode 100644
index 0000000..e70504f
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionManagerCallback.aidl
@@ -0,0 +1,15 @@
+package android.media.musicrecognition;
+
+import android.os.Bundle;
+import android.media.MediaMetadata;
+
+/**
+ * Callback used by system server to notify invoker of {@link MusicRecognitionManager} of the result
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionManagerCallback {
+    void onRecognitionSucceeded(in MediaMetadata result, in Bundle extras);
+    void onRecognitionFailed(int failureCode);
+    void onAudioStreamClosed();
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
new file mode 100644
index 0000000..26543ed
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
@@ -0,0 +1,18 @@
+package android.media.musicrecognition;
+
+import android.media.AudioFormat;
+import android.os.ParcelFileDescriptor;
+import android.os.IBinder;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+
+/**
+ * Interface from the system to a {@link MusicRecognitionService}.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionService {
+  void onAudioStreamStarted(
+      in ParcelFileDescriptor fd,
+      in AudioFormat audioFormat,
+      in IMusicRecognitionServiceCallback callback);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
new file mode 100644
index 0000000..15215c4
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
@@ -0,0 +1,15 @@
+package android.media.musicrecognition;
+
+import android.os.Bundle;
+import android.media.MediaMetadata;
+
+/**
+ * Interface from a {@MusicRecognitionService} the system.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionServiceCallback {
+  void onRecognitionSucceeded(in MediaMetadata result, in Bundle extras);
+
+  void onRecognitionFailed(int failureCode);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionManager.java b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
new file mode 100644
index 0000000..c7f55df
--- /dev/null
+++ b/media/java/android/media/musicrecognition/MusicRecognitionManager.java
@@ -0,0 +1,186 @@
+/*
+ * 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 android.media.musicrecognition;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * System service that manages music recognition.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+@SystemService(Context.MUSIC_RECOGNITION_SERVICE)
+public class MusicRecognitionManager {
+
+    /**
+     * Error code provided by RecognitionCallback#onRecognitionFailed()
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RECOGNITION_FAILED_"},
+            value = {RECOGNITION_FAILED_UNKNOWN,
+                    RECOGNITION_FAILED_NOT_FOUND,
+                    RECOGNITION_FAILED_NO_CONNECTIVITY,
+                    RECOGNITION_FAILED_SERVICE_UNAVAILABLE,
+                    RECOGNITION_FAILED_SERVICE_KILLED,
+                    RECOGNITION_FAILED_TIMEOUT,
+                    RECOGNITION_FAILED_AUDIO_UNAVAILABLE})
+    public @interface RecognitionFailureCode {
+    }
+
+    /** Catchall error code. */
+    public static final int RECOGNITION_FAILED_UNKNOWN = -1;
+    /** Recognition was performed but no result could be identified. */
+    public static final int RECOGNITION_FAILED_NOT_FOUND = 1;
+    /** Recognition failed because the server couldn't be reached. */
+    public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2;
+    /**
+     * Recognition was not possible because the application which provides it is not available (for
+     * example, disabled).
+     */
+    public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3;
+    /** Recognition failed because the recognizer was killed. */
+    public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5;
+    /** Recognition attempt timed out. */
+    public static final int RECOGNITION_FAILED_TIMEOUT = 6;
+    /** Recognition failed due to an issue with obtaining an audio stream. */
+    public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7;
+
+    /** Callback interface for the caller of this api. */
+    public interface RecognitionCallback {
+        /**
+         * Should be invoked by receiving app with the result of the search.
+         *
+         * @param recognitionRequest original request that started the recognition
+         * @param result result of the search
+         * @param extras extra data to be supplied back to the caller. Note that all
+         *               executable parameters and file descriptors would be removed from the
+         *               supplied bundle
+         */
+        void onRecognitionSucceeded(@NonNull RecognitionRequest recognitionRequest,
+                @NonNull MediaMetadata result, @Nullable Bundle extras);
+
+        /**
+         * Invoked when the search is not successful (possibly but not necessarily due to error).
+         *
+         * @param recognitionRequest original request that started the recognition
+         * @param failureCode failure code describing reason for failure
+         */
+        void onRecognitionFailed(@NonNull RecognitionRequest recognitionRequest,
+                @RecognitionFailureCode int failureCode);
+
+        /**
+         * Invoked by the system once the audio stream is closed either due to error, reaching the
+         * limit, or the remote service closing the stream.  Always called per
+         * #beingStreamingSearch() invocation.
+         */
+        void onAudioStreamClosed();
+    }
+
+    private final IMusicRecognitionManager mService;
+
+    /** @hide */
+    public MusicRecognitionManager(IMusicRecognitionManager service) {
+        mService = service;
+    }
+
+    /**
+     * Constructs an {@link android.media.AudioRecord} from the given parameters and streams the
+     * audio bytes to the designated cloud lookup service.  After the lookup is done, the given
+     * callback will be invoked by the system with the result or lack thereof.
+     *
+     * @param recognitionRequest audio parameters for the stream to search
+     * @param callbackExecutor where the callback is invoked
+     * @param callback invoked when the result is available
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION)
+    public void beginStreamingSearch(
+            @NonNull RecognitionRequest recognitionRequest,
+            @NonNull @CallbackExecutor Executor callbackExecutor,
+            @NonNull RecognitionCallback callback) {
+        try {
+            mService.beginRecognition(
+                    requireNonNull(recognitionRequest),
+                    new MusicRecognitionCallbackWrapper(
+                            requireNonNull(recognitionRequest),
+                            requireNonNull(callback),
+                            requireNonNull(callbackExecutor)));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final class MusicRecognitionCallbackWrapper extends
+            IMusicRecognitionManagerCallback.Stub {
+
+        @NonNull
+        private final RecognitionRequest mRecognitionRequest;
+        @NonNull
+        private final RecognitionCallback mCallback;
+        @NonNull
+        private final Executor mCallbackExecutor;
+
+        MusicRecognitionCallbackWrapper(
+                RecognitionRequest recognitionRequest,
+                RecognitionCallback callback,
+                Executor callbackExecutor) {
+            mRecognitionRequest = recognitionRequest;
+            mCallback = callback;
+            mCallbackExecutor = callbackExecutor;
+        }
+
+        @Override
+        public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
+            mCallbackExecutor.execute(
+                    () -> mCallback.onRecognitionSucceeded(mRecognitionRequest, result, extras));
+        }
+
+        @Override
+        public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
+            mCallbackExecutor.execute(
+                    () -> mCallback.onRecognitionFailed(mRecognitionRequest, failureCode));
+        }
+
+        @Override
+        public void onAudioStreamClosed() {
+            mCallbackExecutor.execute(mCallback::onAudioStreamClosed);
+        }
+    }
+}
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
new file mode 100644
index 0000000..b75d2c4
--- /dev/null
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.media.musicrecognition;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.Service;
+import android.content.Intent;
+import android.media.AudioFormat;
+import android.media.MediaMetadata;
+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.util.Log;
+
+/**
+ * Implemented by an app that wants to offer music search lookups. The system will start the
+ * service and stream up to 16 seconds of audio over the given file descriptor.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public abstract class MusicRecognitionService extends Service {
+
+    private static final String TAG = MusicRecognitionService.class.getSimpleName();
+
+    /** Callback for the result of the remote search. */
+    public interface Callback {
+        /**
+         * Call this method to pass back a successful search result.
+         *
+         * @param result successful result of the search
+         * @param extras extra data to be supplied back to the caller. Note that all executable
+         *               parameters and file descriptors would be removed from the supplied bundle
+         */
+        void onRecognitionSucceeded(@NonNull MediaMetadata result, @Nullable Bundle extras);
+
+        /**
+         * Call this method if the search does not find a result on an error occurred.
+         */
+        void onRecognitionFailed(@MusicRecognitionManager.RecognitionFailureCode int failureCode);
+    }
+
+    /**
+     * Action used to start this service.
+     *
+     * @hide
+     */
+    public static final String ACTION_MUSIC_SEARCH_LOOKUP =
+            "android.service.musicrecognition.MUSIC_RECOGNITION";
+
+    private Handler mHandler;
+    private final IMusicRecognitionService mServiceInterface =
+            new IMusicRecognitionService.Stub() {
+                @Override
+                public void onAudioStreamStarted(ParcelFileDescriptor fd,
+                        AudioFormat audioFormat,
+                        IMusicRecognitionServiceCallback callback) {
+                    mHandler.sendMessage(
+                            obtainMessage(MusicRecognitionService.this::onRecognize, fd,
+                                    audioFormat,
+                                    new Callback() {
+                                        @Override
+                                        public void onRecognitionSucceeded(
+                                                @NonNull MediaMetadata result,
+                                                @Nullable Bundle extras) {
+                                            try {
+                                                callback.onRecognitionSucceeded(result, extras);
+                                            } catch (RemoteException e) {
+                                                e.rethrowFromSystemServer();
+                                            }
+                                        }
+
+                                        @Override
+                                        public void onRecognitionFailed(int failureCode) {
+                                            try {
+                                                callback.onRecognitionFailed(failureCode);
+                                            } catch (RemoteException e) {
+                                                e.rethrowFromSystemServer();
+                                            }
+                                        }
+                                    }));
+                }
+            };
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    /**
+     * Read audio from this stream. You must invoke the callback whether the music is recognized or
+     * not.
+     *
+     * @param stream containing music to be recognized. Close when you are finished.
+     * @param audioFormat describes sample rate, channels and endianness of the stream
+     * @param callback to invoke after lookup is finished. Must always be called.
+     */
+    public abstract void onRecognize(@NonNull ParcelFileDescriptor stream,
+            @NonNull AudioFormat audioFormat,
+            @NonNull Callback callback);
+
+    /**
+     * @hide
+     */
+    @Nullable
+    @Override
+    public IBinder onBind(@NonNull Intent intent) {
+        if (ACTION_MUSIC_SEARCH_LOOKUP.equals(intent.getAction())) {
+            return mServiceInterface.asBinder();
+        }
+        Log.w(TAG,
+                "Tried to bind to wrong intent (should be " + ACTION_MUSIC_SEARCH_LOOKUP + ": "
+                        + intent);
+        return null;
+    }
+}
diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.aidl b/media/java/android/media/musicrecognition/RecognitionRequest.aidl
new file mode 100644
index 0000000..757b5701
--- /dev/null
+++ b/media/java/android/media/musicrecognition/RecognitionRequest.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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 android.media.musicrecognition;
+
+parcelable RecognitionRequest;
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.java b/media/java/android/media/musicrecognition/RecognitionRequest.java
new file mode 100644
index 0000000..65b2ccd
--- /dev/null
+++ b/media/java/android/media/musicrecognition/RecognitionRequest.java
@@ -0,0 +1,175 @@
+/*
+ * 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 android.media.musicrecognition;
+
+import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encapsulates parameters for making music recognition queries via {@link MusicRecognitionManager}.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class RecognitionRequest implements Parcelable {
+    @NonNull private final AudioAttributes mAudioAttributes;
+    @NonNull private final AudioFormat mAudioFormat;
+    private final int mCaptureSession;
+    private final int mMaxAudioLengthSeconds;
+    private final int mIgnoreBeginningFrames;
+
+    private RecognitionRequest(Builder b) {
+        mAudioAttributes = requireNonNull(b.mAudioAttributes);
+        mAudioFormat = requireNonNull(b.mAudioFormat);
+        mCaptureSession = b.mCaptureSession;
+        mMaxAudioLengthSeconds = b.mMaxAudioLengthSeconds;
+        mIgnoreBeginningFrames = b.mIgnoreBeginningFrames;
+    }
+
+    @NonNull
+    public AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    @NonNull
+    public AudioFormat getAudioFormat() {
+        return mAudioFormat;
+    }
+
+    public int getCaptureSession() {
+        return mCaptureSession;
+    }
+
+    @SuppressWarnings("MethodNameUnits")
+    public int getMaxAudioLengthSeconds() {
+        return mMaxAudioLengthSeconds;
+    }
+
+    public int getIgnoreBeginningFrames() {
+        return mIgnoreBeginningFrames;
+    }
+
+    /**
+     * Builder for constructing StreamSearchRequest objects.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final class Builder {
+        private AudioFormat mAudioFormat = new AudioFormat.Builder()
+                .setSampleRate(16000)
+                .setEncoding(ENCODING_PCM_16BIT)
+                .build();
+        private AudioAttributes mAudioAttributes = new AudioAttributes.Builder()
+                .setContentType(CONTENT_TYPE_MUSIC)
+                .build();
+        private int mCaptureSession = MediaRecorder.AudioSource.MIC;
+        private int mMaxAudioLengthSeconds = 24; // Max enforced in system server.
+        private int mIgnoreBeginningFrames = 0;
+
+        /** Attributes passed to the constructed {@link AudioRecord}. */
+        @NonNull
+        public Builder setAudioAttributes(@NonNull AudioAttributes audioAttributes) {
+            mAudioAttributes = audioAttributes;
+            return this;
+        }
+
+        /** AudioFormat passed to the constructed {@link AudioRecord}. */
+        @NonNull
+        public Builder setAudioFormat(@NonNull AudioFormat audioFormat) {
+            mAudioFormat = audioFormat;
+            return this;
+        }
+
+        /** Constant from {@link android.media.MediaRecorder.AudioSource}. */
+        @NonNull
+        public Builder setCaptureSession(int captureSession) {
+            mCaptureSession = captureSession;
+            return this;
+        }
+
+        /** Maximum number of seconds to stream from the audio source. */
+        @NonNull
+        public Builder setMaxAudioLengthSeconds(int maxAudioLengthSeconds) {
+            mMaxAudioLengthSeconds = maxAudioLengthSeconds;
+            return this;
+        }
+
+        /** Number of samples to drop from the start of the stream. */
+        @NonNull
+        public Builder setIgnoreBeginningFrames(int ignoreBeginningFrames) {
+            mIgnoreBeginningFrames = ignoreBeginningFrames;
+            return this;
+        }
+
+        /** Returns the constructed request. */
+        @NonNull
+        public RecognitionRequest build() {
+            return new RecognitionRequest(this);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mAudioFormat, flags);
+        dest.writeParcelable(mAudioAttributes, flags);
+        dest.writeInt(mCaptureSession);
+        dest.writeInt(mMaxAudioLengthSeconds);
+        dest.writeInt(mIgnoreBeginningFrames);
+    }
+
+    private RecognitionRequest(Parcel in) {
+        mAudioFormat = in.readParcelable(AudioFormat.class.getClassLoader());
+        mAudioAttributes = in.readParcelable(AudioAttributes.class.getClassLoader());
+        mCaptureSession = in.readInt();
+        mMaxAudioLengthSeconds = in.readInt();
+        mIgnoreBeginningFrames = in.readInt();
+    }
+
+    @NonNull public static final Creator<RecognitionRequest> CREATOR =
+            new Creator<RecognitionRequest>() {
+
+        @Override
+        public RecognitionRequest createFromParcel(Parcel p) {
+            return new RecognitionRequest(p);
+        }
+
+        @Override
+        public RecognitionRequest[] newArray(int size) {
+            return new RecognitionRequest[size];
+        }
+    };
+}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 4380c13..ed99fad 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,7 +25,7 @@
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
 import android.media.tv.ITvInputManagerCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvContentRatingSystemInfo;
 import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvInputInfo;
@@ -89,7 +89,7 @@
     void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
     void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
 
-    List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+    List<TunedInfo> getCurrentTunedInfos(int userId);
 
     // For the recording session
     void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 9f80bf5..3128ba7 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,7 +16,7 @@
 
 package android.media.tv;
 
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvInputInfo;
 
 /**
@@ -29,5 +29,5 @@
     void onInputUpdated(in String inputId);
     void onInputStateChanged(in String inputId, int state);
     void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
-    void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
+    void onCurrentTunedInfosUpdated(in List<TunedInfo> currentTunedInfos);
 }
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TunedInfo.aidl
similarity index 95%
rename from media/java/android/media/tv/TvChannelInfo.aidl
rename to media/java/android/media/tv/TunedInfo.aidl
index 71cd0a7..b7c1d52 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/media/java/android/media/tv/TunedInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.media.tv;
 
-parcelable TvChannelInfo;
+parcelable TunedInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TunedInfo.java
similarity index 88%
rename from media/java/android/media/tv/TvChannelInfo.java
rename to media/java/android/media/tv/TunedInfo.java
index 11cb1f7..6fc5784 100644
--- a/media/java/android/media/tv/TvChannelInfo.java
+++ b/media/java/android/media/tv/TunedInfo.java
@@ -31,11 +31,12 @@
 
 
 /**
- * This class is used to specify information of a TV channel.
+ * Contains information about a {@link TvInputService.Session} that is currently tuned to a channel
+ * or pass-through input.
  * @hide
  */
-public final class TvChannelInfo implements Parcelable {
-    static final String TAG = "TvChannelInfo";
+public final class TunedInfo implements Parcelable {
+    static final String TAG = "TunedInfo";
 
     /**
      * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as
@@ -67,21 +68,21 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface AppType {}
 
-    public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
-            new Parcelable.Creator<TvChannelInfo>() {
+    public static final @NonNull Parcelable.Creator<TunedInfo> CREATOR =
+            new Parcelable.Creator<TunedInfo>() {
                 @Override
-                public TvChannelInfo createFromParcel(Parcel source) {
+                public TunedInfo createFromParcel(Parcel source) {
                     try {
-                        return new TvChannelInfo(source);
+                        return new TunedInfo(source);
                     } catch (Exception e) {
-                        Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+                        Log.e(TAG, "Exception creating TunedInfo from parcel", e);
                         return null;
                     }
                 }
 
                 @Override
-                public TvChannelInfo[] newArray(int size) {
-                    return new TvChannelInfo[size];
+                public TunedInfo[] newArray(int size) {
+                    return new TunedInfo[size];
                 }
             };
 
@@ -94,7 +95,7 @@
     private final int mAppTag;
 
     /** @hide */
-    public TvChannelInfo(
+    public TunedInfo(
             String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
             boolean isForeground, @AppType int appType, int appTag) {
         mInputId = inputId;
@@ -106,7 +107,7 @@
     }
 
 
-    private TvChannelInfo(Parcel source) {
+    private TunedInfo(Parcel source) {
         mInputId = source.readString();
         String uriString = source.readString();
         mChannelUri = uriString == null ? null : Uri.parse(uriString);
@@ -194,11 +195,11 @@
 
     @Override
     public boolean equals(Object o) {
-        if (!(o instanceof TvChannelInfo)) {
+        if (!(o instanceof TunedInfo)) {
             return false;
         }
 
-        TvChannelInfo other = (TvChannelInfo) o;
+        TunedInfo other = (TunedInfo) o;
 
         return TextUtils.equals(mInputId, other.getInputId())
                 && Objects.equals(mChannelUri, other.mChannelUri)
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index c80f3c6..73539fb 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -901,12 +901,13 @@
         }
 
         /**
-         * This is called when the information about current TV channels has been updated.
+         * This is called when the information about current tuned information has been updated.
          *
-         * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels.
+         * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information.
          * @hide
          */
-        public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) {
+        public void onCurrentTunedInfosUpdated(
+                @NonNull List<TunedInfo> tunedInfos) {
         }
     }
 
@@ -968,12 +969,11 @@
             });
         }
 
-        public void postCurrentTvChannelInfosUpdated(
-                final List<TvChannelInfo> currentTvChannelInfos) {
+        public void onCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+                    mCallback.onCurrentTunedInfosUpdated(currentTunedInfos);
                 }
             });
         }
@@ -1283,10 +1283,10 @@
             }
 
             @Override
-            public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+            public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) {
                 synchronized (mLock) {
                     for (TvInputCallbackRecord record : mCallbackRecords) {
-                        record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+                        record.onCurrentTunedInfosUpdated(currentTunedInfos);
                     }
                 }
             }
@@ -1981,18 +1981,18 @@
     }
 
     /**
-     * Returns the list of TV channel information for {@link TvInputService.Session} that are
+     * Returns the list of session information for {@link TvInputService.Session} that are
      * currently in use.
      * <p> Permission com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS is required to get
-     * the channel URIs. If the permission is not granted, {@link TvChannelInfo#getChannelUri()}
-     * returns {@code null}.
+     * the channel URIs. If the permission is not granted,
+     * {@link TunedInfo#getChannelUri()} returns {@code null}.
      * @hide
      */
     @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
     @NonNull
-    public List<TvChannelInfo> getCurrentTvChannelInfos() {
+    public List<TunedInfo> getCurrentTunedInfos() {
         try {
-            return mService.getCurrentTvChannelInfos(mUserId);
+            return mService.getCurrentTunedInfos(mUserId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 0b0e162..71c86cc 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2662,7 +2662,7 @@
     gFields.cryptoInfoModeID = env->GetFieldID(clazz.get(), "mode", "I");
     CHECK(gFields.cryptoInfoModeID != NULL);
 
-    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "pattern",
+    gFields.cryptoInfoPatternID = env->GetFieldID(clazz.get(), "mPattern",
         "Landroid/media/MediaCodec$CryptoInfo$Pattern;");
     CHECK(gFields.cryptoInfoPatternID != NULL);
 
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 45c49e5..0d53ab1 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -331,7 +331,7 @@
     }
 
     if (deviceType != AUDIO_DEVICE_NONE) {
-        device.mType = deviceType;
+        device.mType = (audio_devices_t)deviceType;
         ScopedUtfChars address(env, deviceAddress);
         device.setAddress(address.c_str());
     }
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index ca3cc855..26725f8 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -200,7 +200,7 @@
     paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
     paa->content_type =
             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
-    paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
+    paa->flags = (audio_flags_mask_t) env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
 
     ALOGV("android_media_SoundPool_native_setup");
     auto *ap = new SoundPool(maxChannels, paa);
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 5a0a50c..4d0c258 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -11,7 +11,8 @@
     static_libs: [
         "android-support-test",
         "mockito-target-minus-junit4",
-        "testng"
+        "testng",
+        "truth-prebuilt",
     ],
 
     platform_apis: true,
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
new file mode 100644
index 0000000..92e4c95
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouteInfoTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.mediaroutertest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.media.MediaRouter;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayAddress;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRouteInfoTest {
+    private TestableRouteInfo mRouteInfo;
+    private static Display sWifiDisplay;
+    private static Display sExternalDisplay;
+    private static Display sInternalDisplay;
+    private static final String FAKE_MAC_ADDRESS = "fake MAC address";
+
+    @BeforeClass
+    public static void setUpOnce() {
+        final DisplayManagerGlobal global = DisplayManagerGlobal.getInstance();
+        final DisplayInfo wifiInfo = new DisplayInfo();
+        wifiInfo.flags = Display.FLAG_PRESENTATION;
+        wifiInfo.type = Display.TYPE_WIFI;
+        wifiInfo.address = DisplayAddress.fromMacAddress(FAKE_MAC_ADDRESS);
+        sWifiDisplay = new Display(global, 2, wifiInfo, new DisplayAdjustments());
+
+        final DisplayInfo externalInfo = new DisplayInfo();
+        externalInfo.flags = Display.FLAG_PRESENTATION;
+        externalInfo.type = Display.TYPE_EXTERNAL;
+        sExternalDisplay = new Display(global, 3, externalInfo,  new DisplayAdjustments());
+
+        final DisplayInfo internalInfo = new DisplayInfo();
+        internalInfo.flags = Display.FLAG_PRESENTATION;
+        internalInfo.type = Display.TYPE_INTERNAL;
+        sInternalDisplay = new Display(global, 4, internalInfo,  new DisplayAdjustments());
+    }
+
+    @Before
+    public void setUp() {
+        mRouteInfo = new TestableRouteInfo();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_notLiveVideo() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mSupportedType = MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_includesLiveVideo() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mSupportedType |= MediaRouter.ROUTE_TYPE_LIVE_AUDIO;
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_noPresentationDisplay() {
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_wifiDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_externalDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_internalDisplayOnly() {
+        mRouteInfo.setPresentationDisplays(sInternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sInternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_addressNotMatch() {
+        mRouteInfo.setPresentationDisplays(sWifiDisplay);
+        mRouteInfo.mDeviceAddress = "Not match";
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isNull();
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsWifiAndExternalDisplays_returnWifiDisplay() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsExternalAndInternalDisplays_returnExternal() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sExternalDisplay);
+    }
+
+    @Test
+    public void testGetPresentationDisplay_containsAllDisplays_returnWifiDisplay() {
+        mRouteInfo.setPresentationDisplays(sExternalDisplay, sInternalDisplay, sWifiDisplay);
+
+        mRouteInfo.updatePresentationDisplay();
+
+        assertThat(mRouteInfo.getPresentationDisplay()).isEqualTo(sWifiDisplay);
+    }
+
+    private static class TestableRouteInfo extends MediaRouter.RouteInfo {
+        private Display[] mDisplays = new Display[0];
+        private int mSupportedType = MediaRouter.ROUTE_TYPE_LIVE_VIDEO;
+        private String mDeviceAddress = FAKE_MAC_ADDRESS;
+        private MediaRouter.RouteInfo mDefaultRouteInfo = null;
+
+        private TestableRouteInfo() {
+            super(null);
+        }
+
+        private void setPresentationDisplays(Display ...displays) {
+            mDisplays = new Display[displays.length];
+            System.arraycopy(displays, 0, mDisplays, 0, displays.length);
+        }
+
+        @Override
+        public Display[] getAllPresentationDisplays() {
+            return mDisplays;
+        }
+
+        @Override
+        public int getSupportedTypes() {
+            return mSupportedType;
+        }
+
+        @Override
+        public String getDeviceAddress() {
+            return mDeviceAddress;
+        }
+
+        @Override
+        public MediaRouter.RouteInfo getDefaultAudioVideo() {
+            return mDefaultRouteInfo;
+        }
+    }
+}
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 56f3906..23035b6 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -346,3 +346,10 @@
 void AImageDecoder_delete(AImageDecoder* decoder) {
     delete toDecoder(decoder);
 }
+
+bool AImageDecoder_isAnimated(AImageDecoder* decoder) {
+    if (!decoder) return false;
+
+    ImageDecoder* imageDecoder = toDecoder(decoder);
+    return imageDecoder->mCodec->codec()->getFrameCount() > 1;
+}
diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt
index 01c1477..af2c455 100644
--- a/native/graphics/jni/libjnigraphics.map.txt
+++ b/native/graphics/jni/libjnigraphics.map.txt
@@ -13,6 +13,7 @@
     AImageDecoder_setTargetSize; # introduced=30
     AImageDecoder_computeSampledSize; # introduced=30
     AImageDecoder_setCrop; # introduced=30
+    AImageDecoder_isAnimated; # introduced=31
     AImageDecoderHeaderInfo_getWidth; # introduced=30
     AImageDecoderHeaderInfo_getHeight; # introduced=30
     AImageDecoderHeaderInfo_getMimeType; # introduced=30
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index a0aa0e0..16df76d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -25438,6 +25438,7 @@
 
   public static final class MediaCodec.CryptoInfo {
     ctor public MediaCodec.CryptoInfo();
+    method @NonNull public android.media.MediaCodec.CryptoInfo.Pattern getPattern();
     method public void set(int, @NonNull int[], @NonNull int[], @NonNull byte[], @NonNull byte[], int);
     method public void setPattern(android.media.MediaCodec.CryptoInfo.Pattern);
     field public byte[] iv;
@@ -26366,7 +26367,7 @@
     method public boolean containsKey(String);
     method public int describeContents();
     method public android.graphics.Bitmap getBitmap(String);
-    method @IntRange(from=0) public int getBitmapDimensionLimit();
+    method @IntRange(from=1) public int getBitmapDimensionLimit();
     method @NonNull public android.media.MediaDescription getDescription();
     method public long getLong(String);
     method public android.media.Rating getRating(String);
@@ -26416,7 +26417,7 @@
     method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
     method public android.media.MediaMetadata.Builder putString(String, String);
     method public android.media.MediaMetadata.Builder putText(String, CharSequence);
-    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
+    method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(@IntRange(from=1) int);
   }
 
   @Deprecated public abstract class MediaMetadataEditor {
@@ -42234,11 +42235,13 @@
     method public long getLastAudiblyAlertedMillis();
     method public String getOverrideGroupKey();
     method public int getRank();
+    method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo();
     method @NonNull public java.util.List<android.app.Notification.Action> getSmartActions();
     method @NonNull public java.util.List<java.lang.CharSequence> getSmartReplies();
     method public int getSuppressedVisualEffects();
     method public int getUserSentiment();
     method public boolean isAmbient();
+    method public boolean isConversation();
     method public boolean isSuspended();
     method public boolean matchesInterruptionFilter();
     field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
@@ -46598,7 +46601,7 @@
     method @Deprecated public int getPhoneCount();
     method public int getPhoneType();
     method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
-    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
     method @Nullable public android.telephony.SignalStrength getSignalStrength();
     method public int getSimCarrierId();
     method @Nullable public CharSequence getSimCarrierIdName();
@@ -46621,7 +46624,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getVoiceNetworkType();
-    method public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
+    method @Nullable public android.net.Uri getVoicemailRingtoneUri(android.telecom.PhoneAccountHandle);
     method public boolean hasCarrierPrivileges();
     method public boolean hasIccCard();
     method @Deprecated public boolean iccCloseLogicalChannel(int);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 6e3cec6..e135a3b 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -1,10 +1,20 @@
 // Signature format: 2.0
 package android.app {
 
+  public class ActivityManager {
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
+    method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener);
+  }
+
   public class AppOpsManager {
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public abstract class HomeVisibilityListener {
+    ctor public HomeVisibilityListener();
+    method public abstract void onHomeVisibilityChanged(boolean);
+  }
+
   public class NotificationManager {
     method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
     field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED";
@@ -53,6 +63,10 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
+  public class MediaMetadataRetriever implements java.lang.AutoCloseable {
+    field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
+  }
+
 }
 
 package android.media.session {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 525670c..515c273 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -38,6 +38,7 @@
     field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE";
     field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE";
     field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+    field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
     field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
     field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
@@ -120,6 +121,7 @@
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION";
     field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
@@ -4557,6 +4559,58 @@
 
 }
 
+package android.media.musicrecognition {
+
+  public class MusicRecognitionManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION) public void beginStreamingSearch(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.musicrecognition.MusicRecognitionManager.RecognitionCallback);
+    field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7
+    field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1
+    field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5
+    field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3
+    field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6
+    field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static interface MusicRecognitionManager.RecognitionCallback {
+    method public void onAudioStreamClosed();
+    method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int);
+    method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public abstract class MusicRecognitionService extends android.app.Service {
+    ctor public MusicRecognitionService();
+    method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback);
+  }
+
+  public static interface MusicRecognitionService.Callback {
+    method public void onRecognitionFailed(int);
+    method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle);
+  }
+
+  public final class RecognitionRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.media.AudioAttributes getAudioAttributes();
+    method @NonNull public android.media.AudioFormat getAudioFormat();
+    method public int getCaptureSession();
+    method public int getIgnoreBeginningFrames();
+    method public int getMaxAudioLengthSeconds();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR;
+  }
+
+  public static final class RecognitionRequest.Builder {
+    ctor public RecognitionRequest.Builder();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest build();
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int);
+    method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int);
+  }
+
+}
+
 package android.media.session {
 
   public final class MediaSessionManager {
@@ -10359,6 +10413,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -10389,6 +10444,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -10439,6 +10495,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
index 023b5b4..6d63e31 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java
@@ -567,6 +567,9 @@
                         Log.e(TAG, "Failed to switch to new user: " + user.id);
                     }
                 }
+                if (mAddUserView != null) {
+                    mAddUserView.setEnabled(true);
+                }
             }
         }
 
diff --git a/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
new file mode 100644
index 0000000..5b96da0
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_turkish_f.kcm
@@ -0,0 +1,366 @@
+# 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.
+
+#
+# Turkish F keyboard layout.
+#
+
+type OVERLAY
+
+map key 12 SLASH
+map key 13 MINUS
+map key 43 COMMA
+map key 51 EQUALS
+map key 52 BACKSLASH
+map key 53 PERIOD
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '+'
+    base:                               '+'
+    shift:                              '*'
+    ralt:                               '\u00ac'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               '\u00b9'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '"'
+    ralt:                               '\u00b2'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '^'
+    ralt:                               '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+    ralt:                               '\u00bc'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+    ralt:                               '\u00bd'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '&'
+    ralt:                               '\u00be'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '\''
+    ralt:                               '{'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '('
+    ralt:                               '['
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              ')'
+    ralt:                               ']'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              '='
+    ralt:                               '}'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+    ralt:                               '\\'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+    ralt:                               '|'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+    ralt:                               '@'
+}
+
+key W {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key E {
+    label:                              '\u011f'
+    base:                               '\u011f'
+    shift, capslock:                    '\u011e'
+}
+
+key R {
+    label:                              '\u0131'
+    base:                               '\u0131'
+    shift, capslock:                    'I'
+    ralt:                               '\u00b6'
+    ralt+shift, ralt+capslock:          '\u00ae'
+}
+
+key T {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+}
+
+key Y {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+    ralt:                               '\u00a5'
+}
+
+key U {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key I {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+}
+
+key O {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+    ralt:                               '\u00f8'
+    ralt+shift, ralt+capslock:          '\u00d8'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+    ralt:                               '\u00a3'
+}
+
+key LEFT_BRACKET {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+    ralt:                               '"'
+}
+
+key RIGHT_BRACKET {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+    ralt:                               '~'
+}
+
+### ROW 3
+
+key A {
+    label:                              '\u0075'
+    base:                               '\u0075'
+    shift, capslock:                    '\u0055'
+    ralt:                               '\u00e6'
+    ralt+shift, ralt+capslock:          '\u00c6'
+}
+
+key S {
+    label:                              'i'
+    base:                               'i'
+    shift, capslock:                    '\u0130'
+    ralt:                               '\u00df'
+    ralt+shift, ralt+capslock:          '\u00a7'
+}
+
+key D {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key F {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+    ralt:                               '\u00aa'
+}
+
+key G {
+    label:                              '\u00fc'
+    base:                               '\u00fc'
+    shift, capslock:                    '\u00dc'
+}
+
+key H {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+    ralt:                               '\u20ba'
+}
+
+key J {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key K {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+}
+
+key SEMICOLON {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+    ralt:                               '\u00b4'
+}
+
+key APOSTROPHE {
+    label:                              '\u015f'
+    base:                               '\u015f'
+    shift, capslock:                    '\u015e'
+}
+
+key COMMA {
+    label:                              'X'
+    base:                               'x'
+    shift:                              'X'
+    ralt:                               '\u0060'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '<'
+    base:                               '<'
+    shift:                              '>'
+    ralt:                               '|'
+    ralt+shift, ralt+capslock:          '\u00a6'
+}
+
+key Z {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+    ralt:                               '\u00ab'
+    ralt+shift, ralt+capslock:          '<'
+}
+
+key X {
+    label:                              '\u00f6'
+    base:                               '\u00f6'
+    shift, capslock:                    '\u00d6'
+    ralt:                               '\u00bb'
+    ralt+shift, ralt+capslock:          '>'
+}
+
+key C {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+    ralt:                               '\u00a2'
+    ralt+shift, ralt+capslock:          '\u00a9'
+}
+
+key V {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+}
+
+key B {
+    label:                              '\u00e7'
+    base:                               '\u00e7'
+    shift, capslock:                    '\u00c7'
+}
+
+key N {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+}
+
+key M {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+    ralt:                               '\u00b5'
+    ralt+shift, ralt+capslock:          '\u00ba'
+}
+
+key EQUALS {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+    ralt:                               '\u00d7'
+}
+
+key BACKSLASH {
+    label:                              '.'
+    base:                               '.'
+    shift, capslock:                    ':'
+    ralt:                               '\u00f7'
+}
+
+key PERIOD {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 9d068fe..1e13940 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -105,6 +105,9 @@
     <!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_turkish">Turkish</string>
 
+    <!-- Turkish keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_turkish_f">Turkish F</string>
+
     <!-- Ukrainian keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_ukrainian">Ukrainian</string>
 
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 6b78b68..976a279 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -132,6 +132,10 @@
             android:label="@string/keyboard_layout_turkish"
             android:keyboardLayout="@raw/keyboard_layout_turkish" />
 
+    <keyboard-layout android:name="keyboard_layout_turkish_f"
+            android:label="@string/keyboard_layout_turkish_f"
+            android:keyboardLayout="@raw/keyboard_layout_turkish_f" />
+
     <keyboard-layout android:name="keyboard_layout_ukrainian"
             android:label="@string/keyboard_layout_ukrainian"
             android:keyboardLayout="@raw/keyboard_layout_ukrainian" />
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 6751fa4..2f97f27 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat jy \'n beperkte profiel kan skep, moet jy \'n skermslot opstel om jou programme en persoonlike data te beskerm."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Stel slot op"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skakel oor na <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 470e780..f0d8a40 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"የተገደበ መገለጫ መፍጠር ከመቻልዎ በፊት መተግበሪያዎችዎን እና የግል ውሂብዎን ለመጠበቅ ቁልፍ ማያ ገጽ ማዋቀር አለብዎት።"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ቁልፍ አዘጋጅ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ወደ <xliff:g id="USER_NAME">%s</xliff:g> ቀይር"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 0b45380..323ba44 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -549,9 +549,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل أن تتمكن من إنشاء ملف شخصي مقيد، يلزمك إعداد تأمين للشاشة لحماية تطبيقاتك وبياناتك الشخصية."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تعيين التأمين"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"التبديل إلى <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index f993dba..7679c64 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইচ ডিফ’ল্ট"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"অক্ষম কৰা আছে"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 6a50661..d0bf0ed 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Məhdudlaşdırılmış profil yaratmadan öncə, Siz tətbiqlərinizi və şəxsi datanızı qorumaq üçün ekran kilidi quraşdırmalısınız."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilid ayarlayın"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> adlı istifadəçiyə keçin"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz defoltu"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index cf988ab..3beb9b8 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Da biste mogli da napravite ograničeni profil, treba da podesite zaključavanje ekrana da biste zaštitili aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Podesi zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Pređi na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Podrazumevano za uređaj"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7f6237d..af6da4e 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш чым вы зможаце стварыць профіль з абмежаваннямi, вам трэба наладзіць блакiроўку экрана для абароны сваiх дадаткаў і асабістай інфармацыі."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Усталёўка блакiроўкi"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Пераключыцца на карыстальніка <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартная прылада"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 747cb26..0097c9c 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Преди да можете да създадете потребителски профил с ограничена функционалност, трябва да настроите заключения екран, за да защитите приложенията и личните си данни."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Задаване на заключване"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Превключване към <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартна настройка за у-вото"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Деактивирано"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1ab88ed..c351542 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"আপনি একটি সীমাবদ্ধযুক্ত প্রোফাইল তৈরি করার আগে, আপনাকে আপনার অ্যাপ্লিকেশন এবং ব্যক্তিগত ডেটা সুরক্ষিত করার জন্য একটি স্ক্রিন লক সেট-আপ করতে হবে।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"লক সেট করুন"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-এ পাল্টান"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index e329c99..3d358730 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prije nego vam se omogući kreiranje ograničenog profila, morate postaviti zaključavanje ekrana da biste zaštitili svoje aplikacije i lične podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Postaviti zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prebaci na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5ffdacd..b1edd78 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Per crear un perfil restringit, has de configurar una pantalla de bloqueig per protegir les aplicacions i les dades personals."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Defineix un bloqueig"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Canvia a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Opció predeter. del dispositiu"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 0aef99f..bdce444c 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Před vytvořením omezeného profilu je nutné nejprve nastavit zámek obrazovky k ochraně aplikací a dat."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavit zámek"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Přepnout na uživatele <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Výchozí nastavení zařízení"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 98068cb..1dc0e4e 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan oprette en begrænset profil, skal du oprette en skærmlås for at beskytte dine apps og personlige data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurer låseskærmen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 0837ad3..a9cdf1f 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Vor dem Erstellen eines eingeschränkten Profils musst du eine Displaysperre einrichten, um deine Apps und personenbezogenen Daten zu schützen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Sperre einrichten"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Zu <xliff:g id="USER_NAME">%s</xliff:g> wechseln"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gerätestandard"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 1f9d977..00b8434 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Προκειμένου να μπορέσετε να δημιουργήσετε ένα περιορισμένο προφίλ, θα πρέπει να δημιουργήσετε ένα κλείδωμα οθόνης για την προστασία των εφαρμογών και των προσωπικών δεδομένων σας."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ορισμός κλειδώματος"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Εναλλαγή σε <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Προεπιλογή συσκευής"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ανενεργή"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index abe61a9..4bb2d8a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 959ad46..05c12d6 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index abe61a9..4bb2d8a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index abe61a9..4bb2d8a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 738dd2a..699ad42 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎Before you can create a restricted profile, you’ll need to set up a screen lock to protect your apps and personal data.‎‏‎‎‏‎"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎Set lock‎‏‎‎‏‎"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‏‎Switch to ‎‏‎‎‏‏‎<xliff:g id="USER_NAME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎Creating new user…‎‏‎‎‏‎"</string>
+    <string name="user_nickname" msgid="262624187455825083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎Nickname‎‏‎‎‏‎"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎Add guest‎‏‎‎‏‎"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎Remove guest‎‏‎‎‏‎"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎Guest‎‏‎‎‏‎"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎Take a photo‎‏‎‎‏‎"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎Choose an image‎‏‎‎‏‎"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‎‏‎‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‎‏‎Select photo‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‎‏‏‎‎Device default‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‎‎‏‏‏‎‏‎‏‎‎Disabled‎‏‎‎‏‎"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎Enabled‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index d1e4fb5..1dc5f5b 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado del dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 9ad71e2..977b469 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 14d3b57..3c2593a 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Enne piiratud profiili loomist peate seadistama lukustusekraani, et oma rakendusi ja isiklikke andmeid kaitsta."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Määra lukk"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Lülita kasutajale <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Seadme vaikeseade"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 4a11aa7..33ce3c7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Profil murriztua sortu aurretik, aplikazioak eta datu pertsonalak babesteko, pantaila blokeatzeko metodo bat konfiguratu beharko duzu."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ezarri blokeoa"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Aldatu <xliff:g id="USER_NAME">%s</xliff:g> erabiltzailera"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 3373f81..0453788 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"قبل از ایجاد یک نمایه محدود، باید یک قفل صفحه را برای محافظت از برنامه‌ها و داده‌های شخصی خود تنظیم کنید."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"تنظیم قفل"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"رفتن به <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیش‌فرض دستگاه"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 3d28f1d..7f85572 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Laitteen oletusasetus"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 87d3de1..1745e02 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5f5be97..8c753ae 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index af43099..414564b 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Funcionamento predeterminado"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 894a14a..634870c 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"તમે પ્રતિબંધિત પ્રોફાઇલ બનાવી શકો તે પહેલાં, તમારે તમારી ઍપ્લિકેશનો અને વ્યક્તિગત ડેટાની સુરક્ષા માટે એક લૉક સ્ક્રીન સેટ કરવાની જરૂર પડશે."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"લૉક સેટ કરો"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> પર સ્વિચ કરો"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ડિવાઇસ ડિફૉલ્ટ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"બંધ છે"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 6e734bc..bd052c0 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन  और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिवाइस की डिफ़ॉल्ट सेटिंग"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 3edc452..3cff480 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prije izrade ograničenog profila trebate postaviti zaključavanje zaslona radi zaštite svojih aplikacija i osobnih podataka."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Postavi zaključavanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prelazak na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d86d88b..854265c 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Mielőtt létrehozhatna egy korlátozott profilt, be kell állítania egy képernyőzárat, hogy megvédje alkalmazásait és személyes adatait."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Képernyőzár beállítása"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Váltás erre: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Alapértelmezett"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index e434cac..35b218e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Նախքան դուք կկարողանաք ստեղծել սահմանափակ պրոֆիլ, դուք պետք է կարգավորեք էկրանի կողպումը` ձեր ծրագրերը և անձնական տվյալները պաշտպանելու համար:"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Կարգավորել կողպումը"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Անցնել <xliff:g id="USER_NAME">%s</xliff:g> պրոֆիլին"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Կանխադրված տարբերակ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index a6f8846..cf6455f 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -156,7 +156,7 @@
     <string name="launch_defaults_none" msgid="8049374306261262709">"Tidak ada setelan default"</string>
     <string name="tts_settings" msgid="8130616705989351312">"Setelan text-to-speech"</string>
     <string name="tts_settings_title" msgid="7602210956640483039">"Keluaran text-to-speech"</string>
-    <string name="tts_default_rate_title" msgid="3964187817364304022">"Laju bicara"</string>
+    <string name="tts_default_rate_title" msgid="3964187817364304022">"Kecepatan ucapan"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Kecepatan teks diucapkan"</string>
     <string name="tts_default_pitch_title" msgid="6988592215554485479">"Tinggi nada"</string>
     <string name="tts_default_pitch_summary" msgid="9132719475281551884">"Memengaruhi nada ucapan yang disintesis"</string>
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum dapat membuat profil yang dibatasi, Anda perlu menyiapkan kunci layar untuk melindungi aplikasi dan data pribadi Anda."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Setel kunci"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Beralih ke <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default perangkat"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index caf2323..b4be963 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Áður en þú getur búið til takmarkað snið þarftu að setja upp skjálás til að vernda forritin þín og persónuleg gögn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Velja lás"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Skipta yfir í <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Sjálfgefin stilling tækis"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 8d18727..69b1b5e 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prima di poter creare un profilo con limitazioni, devi impostare un blocco schermo per proteggere le tue app e i tuoi dati personali."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Imposta blocco"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Passa a <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fff881c..158872d 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"לפני שתוכל ליצור פרופיל מוגבל, תצטרך להגדיר נעילת מסך כדי להגן על האפליקציות ועל הנתונים האישיים שלך."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"הגדרת נעילה"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"מעבר אל <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ברירת המחדל של המכשיר"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 5e579b7..13fd53f 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"制限付きプロファイルを作成する場合は、アプリや個人データを保護するように画面ロックを設定しておく必要があります。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ロックを設定"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> に切り替え"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 1b5fae9..8302b8d 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"შეზღუდული პროფილის შექმნამდე, საკუთარი აპლიკაციებისა და პირადი მონაცემების დასაცავად, უნდა დაბლოკოთ ეკრანი."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"საკეტის დაყენება"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-ზე გადართვა"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"მიმდინარეობს ახალი მომხმარებლის შექმნა…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"მოწყობილობის ნაგულისხმევი"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"გათიშული"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ჩართული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 9c290e9..f411d00 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Құрылғыны әдепкісінше реттеу"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2878db1..9b7cc4c 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"មុន​ពេល​អ្នក​អាច​បង្កើត​ប្រវត្តិ​រូប​បាន​ដាក់​កម្រិត អ្នក​ត្រូវ​រៀបចំ​ការ​ចាក់​សោ​អេក្រង់ ដើម្បី​ការពារ​កម្មវិធី និង​ទិន្នន័យ​ផ្ទាល់ខ្លួន​របស់​អ្នក។"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"កំណត់​ការ​ចាក់​សោ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ប្ដូរទៅ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើម​របស់ឧបករណ៍"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 7b01056..1f74d9b6 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್‌ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ಸಾಧನದ ಡೀಫಾಲ್ಟ್"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index f13d9b8..d130834 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"제한된 프로필을 만들기 전에 화면 잠금을 설정하여 앱과 개인 데이터를 보호해야 합니다."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"잠금 설정"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>(으)로 전환"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"기기 기본값"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 419c18f..5672913 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чектелген профайл түзөөрдөн мурун, сиз өзүңүздүн колдонмолоруңузду жана жеке маалыматтарыңызды коргош үчүн, бөгөттөө көшөгөсүн орнотушуңуз керек болот."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Бөгөт коюу"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> аккаунтуна которулуу"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index a72861e..b3b3c81 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ກ່ອນທ່ານຈະສ້າງໂປຣໄຟລ໌ທີ່ຖືກຈຳກັດນັ້ນ, ທ່ານຈະຕ້ອງຕັ້ງຄ່າການລັອກໜ້າຈໍ ເພື່ອປ້ອງກັນແອັບຯ ແລະຂໍ້ມູນສ່ວນໂຕຂອງທ່ານກ່ອນ."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ຕັ້ງການລັອກ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"ສະຫຼັບໄປ <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index c72bf21..2e5385e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Prieš kuriant apribotą profilį reikės nustatyti ekrano užraktą, kad apsaugotumėte programas ir asmeninius duomenis."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nustatyti užraktą"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Perjungti į <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Numatyt. įrenginio nustatymas"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d95e57d..f4fe8e3 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Lai varētu izveidot ierobežotu profilu, jums jāiestata ekrāna bloķēšana, kas aizsargās jūsu lietotni un personas datus."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Iestatīt bloķēšanu"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Pārslēgties uz: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ierīces noklusējums"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 58d4af1..1e933fb 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Пред да може да создадете ограничен профил, треба да поставите заклучување на екранот за да ги заштити вашите апликации и лични податоци."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Постави заклучување"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Префрли на <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандардно за уредот"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 3c281c8..970f778 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ഒരു നിയന്ത്രിത പ്രൊഫൈൽ സൃഷ്‌ടിക്കുന്നതിനുമുമ്പ്, നിങ്ങളുടെ അപ്ലിക്കേഷനുകളും വ്യക്തിഗത ഡാറ്റയും പരിരക്ഷിക്കുന്നതിന് ഒരു സ്‌ക്രീൻ ലോക്ക് സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ലോക്ക് സജ്ജീകരിക്കുക"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> എന്നതിലേക്ക് മാറുക"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ഉപകരണത്തിന്റെ ഡിഫോൾട്ട് പ്രവർത്തനം"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 37fc5b4..3dc4989 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Та хязгаарлагдсан профайл үүсгэхийн өмнө өөрийн апп-ууд болон хувийн өгөгдлийг хамгаалахын тулд дэлгэцийн түгжээг тохируулах шаардлагатай."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Түгжээг тохируулах"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> руу сэлгэх"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 360f158..7e418a4 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"तुम्ही एक प्रतिबंधित प्रोफाईल तयार करु शकण्यापूर्वी तुम्हाला तुमचे अ‍ॅप्स आणि वैयक्तिक डेटा संरक्षित करण्यासाठी एक स्क्रीन लॉक सेट करण्याची आवश्यकता राहील."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करा"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> वर स्विच करा"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 68356df2..4c23a84 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum anda boleh membuat profil yang terhad, anda perlu menyediakan kunci skrin untuk melindungi apl dan data peribadi anda."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Tetapkan kunci"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Tukar kepada <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Lalai peranti"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3729a83..aa3cf08 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ကန့်သတ်ကိုယ်ရေးအချက်အလက်တစ်ခုကို မပြုလုပ်မီ သင်၏ အပလီကေးရှင်းများနှင့် ကိုယ်ပိုင်အချက်အလက်များကို ကာကွယ်ရန် မျက်နှာပြင်သော့ချခြင်းကို စီမံရန် လိုအပ်လိမ့်မည်"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"သော့ချရန် သတ်မှတ်ပါ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> သို့ ပြောင်းရန်"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"စက်ပစ္စည်းမူရင်း"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0e0e761..0c5431b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan opprette en begrenset profil, må du konfigurere skjermlåsen for å beskytte appene og de personlige dataene dine."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Angi lås"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Bytt til <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Standard for enheten"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index ee368fd..4c230d3 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको एप र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"पूर्वनिर्धारित यन्त्र"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 27f5dc9..ae734a5 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat je een beperkt profiel kunt maken, moet je een schermvergrendeling instellen om je apps en persoonsgegevens te beschermen."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Vergrendeling instellen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Overschakelen naar <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgeschakeld"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d200f50..18a88fa 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍‌ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍‌ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍‌ ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍‌ ସେଟ୍‌ କରନ୍ତୁ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 354ee12..b1bde27 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਤੁਸੀਂ ਇੱਕ ਪ੍ਰਤਿਬੰਧਿਤ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕੋ, ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਐਪਾਂ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਇੱਕ ਸਕ੍ਰੀਨ  ਲਾਕ  ਸੈੱਟ ਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">" ਲਾਕ  ਸੈੱਟ ਕਰੋ"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> \'ਤੇ ਜਾਓ"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 095412c..7ea2d0c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Zanim utworzysz profil z ograniczeniami, musisz skonfigurować ekran blokady, by chronić aplikacje i osobiste dane."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Ustaw blokadę"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Przełącz na: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ustawienie domyślne urządzenia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 895a987..102961a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de criar um perfil restrito, configure um bloqueio de tela para proteger seus apps e seus dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 2d9f037..de536b2 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de poder criar um perfil restrito, tem de configurar um bloqueio de ecrã para proteger as suas aplicações e dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 895a987..102961a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de criar um perfil restrito, configure um bloqueio de tela para proteger seus apps e seus dados pessoais."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Padrão do dispositivo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativado"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativado"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 728db17..934ef64 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Treceți la <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Prestabilit pentru dispozitiv"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ff2115e..e75ee7f 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Вариант по умолчанию"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index a883cc6..dc9b702 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"සීමිත පැතිකඩක් නිර්මාණය කිරීමට කලින්. ඔබගේ යෙදුම් සහ පෞද්ගලික දත්ත ආරක්ෂා කිරීමට තිර අගුලක් සැකසිය යුතුයි."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"අගුල සකසන්න"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> වෙත මාරු වන්න"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"උපාංගයේ පෙරනිමිය"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 05c6379..203f253 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Pred vytvorením obmedzeného profilu je nutné najprv nastaviť zámku obrazovky na ochranu aplikácií a osobných údajov."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastaviť uzamknutie"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Prepnúť na používateľa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index fd216e8..3bfda06 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Preklop na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Privzeta nastavitev naprave"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 002c7fc..859cc70 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Para se të mund të krijosh një profil të kufizuar, duhet të konfigurosh një kyçje të ekranit për të mbrojtur aplikacionet dhe të dhënat e tua personale."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Cakto kyçjen"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Kalo te <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 25a1beb7..6c02c3c 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -546,9 +546,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Да бисте могли да направите ограничени профил, треба да подесите закључавање екрана да бисте заштитили апликације и личне податке."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Подеси закључавање"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Пређи на корисника <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Подразумевано за уређај"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Онемогућено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 352cb0a..dc21675 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Innan du skapar en begränsad profil måste du konfigurera ett skärmlås för att skydda dina appar och personliga data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurera lås"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Byt till <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhetens standardinställning"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index c5d70ac..8f80e55 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kabla uunde wasifu uliowekekwa vikwazo, utahitajika kuweka skrini iliyofungwa ili kulinda programu zako na data binafsi."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Weka ufunguo"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 7837dd8..1069421 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் ஆப்ஸ் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"பூட்டை அமை"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>க்கு மாறு"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"சாதனத்தின் இயல்புநிலை"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index e252eca..3ad2375 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్‌ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్‌ను సెటప్ చేయాల్సి ఉంటుంది."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్‌ను సెట్ చేయి"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"పరికర ఆటోమేటిక్ సెట్టింగ్"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"డిజేబుల్ చేయబడింది"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 7468d04..cc0ac74 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ค่าเริ่มต้นของอุปกรณ์"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ปิดใช้"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 5d4e975..77ab7cf 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Bago ka makakalikha ng pinaghihigpitang profile, kakailanganin mong mag-set up ng screen lock upang protektahan ang iyong apps at personal na data."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Itakda ang lock"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Lumipat sa <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default ng device"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index f01f3fa..9d980c4 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Kısıtlanmış bir profil oluşturabilmeniz için uygulamalarınızı ve kişisel verilerinizi korumak üzere bir ekran kilidi oluşturmanız gerekir."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Kilidi ayarla"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> hesabına geç"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 9ca2f06..9a7b89f 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -547,9 +547,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Перш ніж створювати обмежений профіль, потрібно налаштувати блокування екрана, щоб захистити свої програми та особисті дані."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Налаштувати блокування"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Перейти до користувача <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 8953f50..1cc0859 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"‫<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f25b3ac..3df33b6 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -545,9 +545,14 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Cheklangan profil yaratish uchun, shaxsiy ilovlar va ma‘lumotlarni himoyalash maqsadida avval ekran qulfini yaratish lozim."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Qulf o‘rnatish"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Bunga almashish: <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
+    <string name="user_nickname" msgid="262624187455825083">"Nik"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
+    <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
+    <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
+    <string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string>
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Qurilma standarti"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Yoqilmagan"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Yoniq"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b5798f3..2ed6ca8 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c4dcfff..024ea79 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"您需要先设置锁定屏幕来保护您的应用和个人数据,然后才可以创建受限个人资料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"设置屏幕锁定方式"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切换到<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"设备默认设置"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index e04651c..75f050f 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"建立限制存取的個人檔案前,您必須先設定上鎖畫面來保護您的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定上鎖畫面"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index a1ae6b6..9866b47 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"如要建立設有限制的個人資料,你必須先設定螢幕鎖定來保護你的應用程式和個人資料。"</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"設定鎖定"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 2dafad8..b1825c4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -545,9 +545,19 @@
     <string name="user_need_lock_message" msgid="4311424336209509301">"Ngaphambi kokuthi ungadala iphrofayela ekhawulelwe, kuzomele usethe ukukhiya isikrini ukuze uvikele izinhlelo zakho zokusebenza nedatha yakho yomuntu siqu."</string>
     <string name="user_set_lock_button" msgid="1427128184982594856">"Setha ukukhiya"</string>
     <string name="user_switch_to_user" msgid="6975428297154968543">"Shintshela ku-<xliff:g id="USER_NAME">%s</xliff:g>"</string>
+    <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) -->
+    <skip />
+    <!-- no translation found for user_nickname (262624187455825083) -->
+    <skip />
     <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
     <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
+    <!-- no translation found for user_image_take_photo (467512954561638530) -->
+    <skip />
+    <!-- no translation found for user_image_choose_photo (1363820919146782908) -->
+    <skip />
+    <!-- no translation found for user_image_photo_selector (433658323306627093) -->
+    <skip />
     <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Idivayisi ezenzakalelayo"</string>
     <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string>
     <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string>
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 81cf118..b0a9136 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -1090,7 +1090,7 @@
 
         // Verify second update AP is the same object as the first update AP
         assertThat(passpointAccessPointsFirstUpdate.get(0))
-                .isSameAs(passpointAccessPointsSecondUpdate.get(0));
+                .isSameInstanceAs(passpointAccessPointsSecondUpdate.get(0));
         // Verify second update AP has the average of the first and second update RSSIs
         assertThat(passpointAccessPointsSecondUpdate.get(0).getRssi())
                 .isEqualTo((prevRssi + newRssi) / 2);
@@ -1210,7 +1210,8 @@
                 providersAndScans, cachedAccessPoints);
 
         // Verify second update AP is the same object as the first update AP
-        assertThat(osuAccessPointsFirstUpdate.get(0)).isSameAs(osuAccessPointsSecondUpdate.get(0));
+        assertThat(osuAccessPointsFirstUpdate.get(0))
+                .isSameInstanceAs(osuAccessPointsSecondUpdate.get(0));
         // Verify second update AP has the average of the first and second update RSSIs
         assertThat(osuAccessPointsSecondUpdate.get(0).getRssi())
                 .isEqualTo((prevRssi + newRssi) / 2);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
index a83d7e0..b392c5e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -69,7 +69,7 @@
 
         assertWithMessage("Intent filter should contain expected intents")
                 .that(ipAddressPreferenceController.getConnectivityIntents())
-                .asList().containsAllIn(expectedIntents);
+                .asList().containsAtLeastElementsIn(expectedIntents);
     }
 
     private static class ConcreteIpAddressPreferenceController extends
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
index 40b9b13..3705267 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -90,7 +90,7 @@
 
         assertWithMessage("Intent filter should contain expected intents")
                 .that(mController.getConnectivityIntents())
-                .asList().containsAllIn(expectedIntents);
+                .asList().containsAtLeastElementsIn(expectedIntents);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 1769053..906e06e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -284,7 +284,7 @@
 
         assertThat(outTiles).hasSize(1);
         final Bundle newMetaData = outTiles.get(0).getMetaData();
-        assertThat(newMetaData).isNotSameAs(oldMetadata);
+        assertThat(newMetaData).isNotSameInstanceAs(oldMetadata);
     }
 
     @Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a927997..bba29db 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -329,6 +329,12 @@
     <!-- Permission needed for CTS test - TimeManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
 
+    <!-- Permission required for CTS test - android.server.biometrics -->
+    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+
+    <!-- Permission required for CTS test - android.server.biometrics -->
+    <uses-permission android:name="android.permission.TEST_BIOMETRIC" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SoundPicker/res/values-uz/strings.xml b/packages/SoundPicker/res/values-uz/strings.xml
index 9018e66..a617733 100644
--- a/packages/SoundPicker/res/values-uz/strings.xml
+++ b/packages/SoundPicker/res/values-uz/strings.xml
@@ -21,7 +21,7 @@
     <string name="alarm_sound_default" msgid="4787646764557462649">"Standart signal tovushi"</string>
     <string name="add_ringtone_text" msgid="6642389991738337529">"Rington qo‘shish"</string>
     <string name="add_alarm_text" msgid="3545497316166999225">"Signal qo‘shish"</string>
-    <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma qo‘shish"</string>
+    <string name="add_notification_text" msgid="4431129543300614788">"Bildirishnoma kiritish"</string>
     <string name="delete_ringtone_text" msgid="201443984070732499">"O‘chirish"</string>
     <string name="unable_to_add_ringtone" msgid="4583511263449467326">"Maxsus rington qo‘shib bo‘lmadi"</string>
     <string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Maxsus ringtonni o‘chirib bo‘lmadi"</string>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 7d3390d..31faedf 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vergrotingvensterkontroles"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoem in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoem uit"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Skuif op"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Skuif af"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Beweeg links"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Beweeg regs"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Toestelkontroles"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Voeg kontroles vir jou gekoppelde toestelle by"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ontkoppel)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kon nie koppel nie. Probeer weer."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bind nuwe toestel saam"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Bounommer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Bounommer is na knipbord gekopieer."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a91182e..41f6bed 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ቅንብሮች"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"የማጉያ መስኮት"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"የማጉያ መስኮት መቆጣጠሪያዎች"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"አጉላ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"አሳንስ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ወደ ላይ ውሰድ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ወደ ታች ውሰድ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ወደ ግራ ውሰድ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ወደ ቀኝ ውሰድ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"የመሣሪያ መቆጣጠሪያዎች"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ለእርስዎ የተገናኙ መሣሪያዎች መቆጣጠሪያዎችን ያክሉ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ግንኙነት ተቋርጧል)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ማገናኘት አልተቻለም። እንደገና ይሞክሩ።"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"አዲስ መሣሪያ ያጣምሩ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"የግንብ ቁጥር"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"የገንባ ቁጥር ወደ ቅንጥብ ሰሌዳ ተቀድቷል።"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 74a9b64..d1f98f0 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1032,17 +1032,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"الإعدادات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"تكبير"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"تصغير"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"نقل للأعلى"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"نقل للأسفل"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"نقل لليسار"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"نقل لليمين"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالأجهزة"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"إضافة عناصر تحكّم لأجهزتك المتصلة"</string>
@@ -1111,8 +1113,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غير متّصل)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"تعذّر الاتصال. يُرجى إعادة المحاولة."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"إقران جهاز جديد"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"رقم الإصدار"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"تم نسخ رقم الإصدار إلى الحافظة."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 297d774..52b6c5a 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ছেটিংসমূহ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"বিবৰ্ধন ৱিণ্ড’"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"বিবৰ্ধন ৱিণ্ড’ৰ নিয়ন্ত্ৰণসমূহ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"জুম ইন কৰক"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"জুম আউট কৰক"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ওপৰলৈ নিয়ক"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"তললৈ নিয়ক"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাওঁফাললৈ নিয়ক"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"সোঁফাললৈ নিয়ক"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইচৰ নিয়ন্ত্ৰণসমূহ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপোনাৰ সংযোজিত ডিভাইচসমূহৰ বাবে নিয়ন্ত্ৰণসমূহ যোগ কৰক"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (সংযোগ বিচ্ছিন্ন হৈছে)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"সংযোগ কৰিব পৰা নগ’ল। পুনৰ চেষ্টা কৰক।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইচ পেয়াৰ কৰক"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ডৰ নম্বৰ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ক্লিপব’ৰ্ডলৈ বিল্ডৰ নম্বৰ প্ৰতিলিপি কৰা হ’ল।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index debad02..b446718 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -797,7 +797,7 @@
     <string name="keyboard_key_page_up" msgid="173914303254199845">"Yuxarı Səhifə"</string>
     <string name="keyboard_key_page_down" msgid="9035902490071829731">"Aşağı Səhifə"</string>
     <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Silin"</string>
-    <string name="keyboard_key_move_home" msgid="3496502501803911971">"Əsas səhifə"</string>
+    <string name="keyboard_key_move_home" msgid="3496502501803911971">"Home"</string>
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Son"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Daxil edin"</string>
     <string name="keyboard_key_num_lock" msgid="7209960042043090548">"Nömrələr"</string>
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Böyütmə Pəncərəsi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Böyütmə Pəncərəsi Kontrolları"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaxınlaşdırın"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaqlaşdırın"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yuxarı köçürün"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı köçürün"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola köçürün"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa köçürün"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz idarəetmələri"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Qoşulmuş cihazlarınız üçün nizamlayıcılar əlavə edin"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlantı kəsilib)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Qoşulmaq alınmadı. Yenə cəhd edin."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihazı qoşalaşdırın"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versiya nömrəsi"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versiya nömrəsi mübadilə buferinə kopyalandı."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index be31c2d..3cc10d8 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Podešavanja"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećajte"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjite"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomerite nagore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomerite nadole"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomerite nalevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomerite nadesno"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspelo. Probajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u privremenu memoriju."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index e3831a8..07e7f8a 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налады"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Акно павелічэння"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Налады акна павелічэння"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Павялічыць маштаб"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Паменшыць маштаб"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перамясціць уверх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перамясціць ніжэй"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перамясціць улева"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перамясціць управа"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Элементы кіравання прыладай"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Дадайце элементы кіравання для падключаных прылад"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (адключана)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не ўдалося падключыцца. Паўтарыце спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спалучыць з новай прыладай"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Нумар зборкі"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Нумар зборкі скапіраваны ў буфер абмену."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d959178..f33c5cf 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за ниво на мащаба"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли за прозореца за ниво на мащаба"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличаване на мащаба"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Намаляване на мащаба"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Преместване нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Преместване надолу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Преместване наляво"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Преместване надясно"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за устройството"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавяне на контроли за свързаните ви устройства"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (връзката е прекратена)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Неуспешно свързване. Опитайте отново."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Сдвояване на ново устройство"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер на компилацията"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номерът на компилацията е копиран в буферната памет."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fffa555..0b5d65e 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"সেটিংস"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"উইন্ডো বড় করে দেখা"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"উইন্ডো কন্ট্রোল বড় করে দেখা"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"বড় করুন"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ছোট করুন"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"উপরে তুলুন"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপনার কানেক্ট করা ডিভাইসের জন্য কন্ট্রোল যোগ করুন"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (কানেক্ট করা নেই)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"কানেক্ট করা যায়নি। আবার চেষ্টা করুন।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"নতুন ডিভাইস পেয়ার করুন"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"বিল্ড নম্বর"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"বিল্ড নম্বর ক্লিপবোর্ডে কপি করা হয়েছে।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index f5410cf..ac68620 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -726,7 +726,7 @@
     <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"Nema nedavnih oblačića"</string>
     <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
     <string name="notification_unblockable_desc" msgid="2073030886006190804">"Ta obavještenja se ne mogu izmijeniti."</string>
-    <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovdje nije moguće konfigurirati ovu grupu obavještenja"</string>
+    <string name="notification_multichannel_desc" msgid="7414593090056236179">"Ovu grupu obavještenja nije moguće konfigurirati ovdje"</string>
     <string name="notification_delegate_header" msgid="1264510071031479920">"Obavještenje preko proksi servera"</string>
     <string name="notification_channel_dialog_title" msgid="6856514143093200019">"Sva obavještenja aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="see_more_title" msgid="7409317011708185729">"Prikaži više"</string>
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za uvećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za uvećavanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Uvećavanje"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Umanjivanje"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pomjeranje prema gore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pomjeranje prema dolje"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pomjeranje lijevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pomjeranje desno"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrole za povezane uređaje"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (veza je prekinuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije uspjelo. Pokušajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uparite novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj verzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj verzije je kopiran u međumemoriju."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 28bf3bb..5e64aee 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuració"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra d\'ampliació"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra de controls d\'ampliació"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Amplia"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Redueix"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mou cap amunt"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mou cap avall"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mou cap a l\'esquerra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mou cap a la dreta"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Controls de dispositius"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Afegeix controls per als teus dispositius connectats"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconnectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No s\'ha pogut connectar. Torna-ho a provar."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincula un dispositiu nou"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilació"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"El número de compilació s\'ha copiat al porta-retalls."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c66f200..5ca5df6 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavení"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Zvětšovací okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládací prvky zvětšovacího okna"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Přiblížit"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddálit"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Přesunout nahoru"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Přesunout dolů"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Přesunout doleva"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Přesunout doprava"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládání zařízení"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Přidejte ovládací prvky pro připojená zařízení"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojeno)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Spojení se nezdařilo. Zkuste to znovu."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovat nové zařízení"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo sestavení"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo sestavení bylo zkopírováno do schránky."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 5b0566d..419740e 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Indstillinger"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vindue med forstørrelse"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vindue med forstørrelsesstyring"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom ind"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ud"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flyt op"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flyt ned"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flyt til venstre"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flyt til højre"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhedsstyring"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tilføj styring af dine tilsluttede enheder"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ingen forbindelse)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Der kunne ikke oprettes forbindelse. Prøv igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Par ny enhed"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Buildnummeret blev kopieret til udklipsholderen."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0ab1f0e..4b1be3b 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Einstellungen"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrößerungsfenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Einstellungen für Vergrößerungsfenster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Heranzoomen"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Herauszoomen"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Nach oben bewegen"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Nach unten bewegen"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Nach links bewegen"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Nach rechts bewegen"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Gerätesteuerung"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Steuerelemente für verbundene Geräte hinzufügen"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nicht verbunden)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Verbindung nicht möglich. Versuch es noch einmal."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Neues Gerät koppeln"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-Nummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build-Nummer in Zwischenablage kopiert."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 6d86e91..fc4e78b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1012,18 +1012,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ρυθμίσεις"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Παράθυρο μεγέθυνσης"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Στοιχεία ελέγχου παραθύρου μεγέθυνσης"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Μεγέθυνση"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Σμίκρυνση"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Μετακίνηση επάνω"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Μετακίνηση κάτω"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Μετακίνηση αριστερά"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Μετακίνηση δεξιά"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Εναλλαγή μεγιστοποίησης"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Μεγέθυνση ολόκληρης της οθόνης"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Στοιχεία ελέγχου συσκευής"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Προσθήκη στοιχείων ελέγχου για τις συνδεδεμένες συσκευές σας."</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Ρύθμιση στοιχείων ελέγχου συσκευής"</string>
@@ -1087,8 +1085,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (αποσυνδέθηκε)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Δεν ήταν δυνατή η σύνδεση. Δοκιμάστε ξανά."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Σύζευξη νέας συσκευής"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Αριθμός έκδοσης"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Ο αριθμός έκδοσης αντιγράφηκε στο πρόχειρο."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index cb03d40..709a506 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 8e5849e..79a2844 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index cb03d40..709a506 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index cb03d40..709a506 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Move down"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Move left"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Move right"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Magnification switch"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Magnify entire screen"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Device controls"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Add controls for your connected devices"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Set up device controls"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index e107ed5..ab6699b 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎Move down‎‏‎‎‏‎"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎‏‏‎Move left‎‏‎‎‏‎"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‎‎‏‎‏‎‎‎‎Move right‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎Magnification switch‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎Magnify entire screen‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎Magnify part of screen‎‏‎‎‏‎"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎Switch‎‏‎‎‏‎"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎Device controls‎‏‎‎‏‎"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‎Add controls for your connected devices‎‏‎‎‏‎"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎Set up device controls‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index be37623..a1e21a1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -1012,18 +1012,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles de ampliación de la ventana"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Acercar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Alejar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Botón de ampliación"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda la pantalla"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Agrega controles para los dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles de dispositivos"</string>
@@ -1087,8 +1085,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se pudo establecer la conexión. Vuelve a intentarlo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo nuevo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Se copió el número de compilación en el portapapeles."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 08b17cc..51c6d0d 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ajustes"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventana de ampliación"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ventana de controles de ampliación"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ampliar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Reducir"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover hacia arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover hacia abajo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover hacia la izquierda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover hacia la derecha"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Añade controles para tus dispositivos conectados"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"No se ha podido conectar. Inténtalo de nuevo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular nuevo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número de compilación copiado en el portapapeles."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index b33ad01..6d616b8 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Seaded"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurendamisaken"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurendamisakna juhtelemendid"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Suumi sisse"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Suumi välja"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Teisalda üles"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Teisalda alla"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Teisalda vasakule"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Teisalda paremale"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisage juhtelemendid ühendatud seadmete jaoks"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (pole ühendatud)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ühenduse loomine ebaõnnestus. Proovige uuesti."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Uue seadme sidumine"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Järgunumber"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Järgunumber kopeeriti lõikelauale."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 3f1a53c..05e52f4 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ezarpenak"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Lupa-leihoa"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Lupa-leihoaren aukerak"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Handitu"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Txikitu"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Eraman gora"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Eraman behera"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Eraman ezkerrera"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Eraman eskuinera"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Gailuak kontrolatzeko widgetak"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Gehitu konektatutako gailuak kontrolatzeko widgetak"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (deskonektatuta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ezin izan da konektatu. Saiatu berriro."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parekatu beste gailu batekin"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Konpilazio-zenbakia"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Kopiatu da konpilazio-zenbakia arbelean."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e5606a2..1c16b33 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -431,7 +431,7 @@
     <string name="quick_settings_screen_record_start" msgid="1574725369331638985">"شروع"</string>
     <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"توقف"</string>
     <string name="media_seamless_remote_device" msgid="177033467332920464">"دستگاه"</string>
-    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامه‌ها،‌ تند به بالا بکشید"</string>
+    <string name="recents_swipe_up_onboarding" msgid="2820265886420993995">"برای تغییر برنامه‌ها،‌ تند به‌بالا بکشید"</string>
     <string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"برای جابه‌جایی سریع میان برنامه‌ها، به چپ بکشید"</string>
     <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"تغییر وضعیت نمای کلی"</string>
     <string name="expanded_header_battery_charged" msgid="5307907517976548448">"شارژ کامل شد"</string>
@@ -450,8 +450,8 @@
     <string name="keyguard_more_overflow_text" msgid="5819512373606638727">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
     <string name="speed_bump_explanation" msgid="7248696377626341060">"اعلان‌های کمتر فوری در زیر"</string>
     <string name="notification_tap_again" msgid="4477318164947497249">"دوباره ضربه بزنید تا باز شود"</string>
-    <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به بالا بکشید"</string>
-    <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به بالا بکشید"</string>
+    <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند به‌بالا بکشید"</string>
+    <string name="keyguard_retry" msgid="886802522584053523">"برای امتحان مجدد، انگشتتان را تند به‌بالا بکشید"</string>
     <string name="do_disclosure_generic" msgid="4896482821974707167">"این دستگاه به سازمان شما تعلق دارد"</string>
     <string name="do_disclosure_with_name" msgid="2091641464065004091">"این دستگاه به <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> تعلق دارد"</string>
     <string name="phone_hint" msgid="6682125338461375925">"انگشتتان را از نماد تلفن تند بکشید"</string>
@@ -740,7 +740,7 @@
     <string name="feedback_promoted" msgid="8075757485407091976">"سیستمْ این اعلان را ارتقا داده است."</string>
     <string name="feedback_demoted" msgid="5848066008939031913">"سیستمْ این اعلان را تنزل داده است."</string>
     <string name="feedback_prompt" msgid="2278631214125128281">"این مورد درست بود؟"</string>
-    <string name="feedback_response" msgid="4671729244976641339">"از بازخورد شما سپاس‌گذاریم!"</string>
+    <string name="feedback_response" msgid="4671729244976641339">"از بازخوردتان سپاس‌گزاریم!"</string>
     <string name="feedback_ok" msgid="6481426753298857144">"تأیید"</string>
     <string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> باز شد"</string>
     <string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"کنترل‌های اعلان برای <xliff:g id="APP_NAME">%1$s</xliff:g> بسته شد"</string>
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"تنظیمات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"پنجره بزرگ‌نمایی"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"کنترل‌های پنجره بزرگ‌نمایی"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"بزرگ کردن"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"کوچک کردن"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"انتقال به بالا"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"انتقال به پایین"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"انتقال به راست"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"انتقال به چپ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"کنترل‌های دستگاه"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"افزودن کنترل‌ها برای دستگاه‌های متصل"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (اتصال قطع شد)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"متصل نشد. دوباره امتحان کنید."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"مرتبط کردن دستگاه جدید"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"شماره ساخت"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"شماره ساخت در بریده‌دان کپی شد."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index e00b50e..1e900fb 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Asetukset"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Suurennusikkuna"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Suurennusikkunan ohjaimet"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Lähennä"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Loitonna"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Siirrä ylös"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Siirrä alas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Siirrä vasemmalle"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Siirrä oikealle"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Laitteiden hallinta"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisää ohjaimia yhdistettyjä laitteita varten"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (yhteys katkaistu)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ei yhteyttä. Yritä uudelleen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Muodosta uusi laitepari"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Koontiversion numero"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Koontiversion numero kopioitu leikepöydälle"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 03e205d..f390693 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Commandes pour la fenêtre d\'agrandissement"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Effectuer un zoom avant"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Effectuer un zoom arrière"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajoutez des commandes pour vos appareils connectés"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un autre appareil"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de version"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Le numéro de version a été copié dans le presse-papiers."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 82df63a..df542bc 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Paramètres"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fenêtre d\'agrandissement"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Fenêtre des commandes d\'agrandissement"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Faire un zoom avant"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Faire un zoom arrière"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Déplacer vers le haut"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Déplacer vers le bas"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Déplacer vers la gauche"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Déplacer vers la droite"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Commandes des appareils"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ajouter des commandes pour vos appareils connectés"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (déconnecté)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossible de se connecter. Réessayez."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Associer un nouvel appareil"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numéro de build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numéro de build copié dans le presse-papiers."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 9be451d..fedbe2c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configuración"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ventá de superposición"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controis de ampliación da ventá"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Achegar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Afastar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover cara arriba"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engade controis para os dispositivos conectados"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (dispositivo desconectado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Non se puido establecer a conexión. Téntao de novo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Vincular dispositivo novo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número de compilación"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Copiouse o número de compilación no portapapeis."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 864d05f..d9c5b72 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"સેટિંગ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"વિસ્તૃતીકરણ વિંડો"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"વિસ્તૃતીકરણ વિંડોના નિયંત્રણો"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"મોટું કરો"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"નાનું કરો"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ઉપર ખસેડો"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"તમારા કનેક્ટ કરેલા ડિવાઇસ માટે નિયંત્રણો ઉમેરો"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ડિસ્કનેક્ટ થયેલું)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"કનેક્ટ કરી શકાયું નહીં. ફરી પ્રયાસ કરો."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"બિલ્ડ નંબર"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"બિલ્ડ નંબર ક્લિપબૉર્ડ પર કૉપિ કર્યો."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index e11055c..08149fb 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1014,17 +1014,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ज़ूम इन करें"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ज़ूम आउट करें"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ऊपर ले जाएं"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"नीचे ले जाएं"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"बाईं ओर ले जाएं"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"दाईं ओर ले जाएं"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"डिवाइस कंट्रोल"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"कनेक्ट किए गए डिवाइस के लिए कंट्रोल जोड़ें"</string>
@@ -1089,8 +1091,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिसकनेक्ट किया गया)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट नहीं किया जा सका. फिर से कोशिश करें."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नया डिवाइस जोड़ें"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर को क्लिपबोर्ड पर कॉपी किया गया."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index d5033fa..b01f58e 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Postavke"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Prozor za povećavanje"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrole prozora za povećavanje"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povećaj"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Smanji"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premjesti gore"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premjesti dolje"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premjesti ulijevo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premjesti udesno"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodavanje kontrola za povezane uređaje"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nije povezano)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezivanje nije bilo moguće. Pokušajte ponovo."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Upari novi uređaj"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Broj međuverzije"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Broj međuverzije kopiran je u međuspremnik."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 6d960e7..1fcabcf 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Beállítások"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Nagyítás ablaka"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Nagyítási vezérlők ablaka"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Nagyítás"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Kicsinyítés"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mozgatás felfelé"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mozgatás lefelé"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mozgatás balra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mozgatás jobbra"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Eszközvezérlők"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Vezérlők hozzáadása a csatlakoztatott eszközökhöz"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (leválasztva)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Sikertelen csatlakozás. Próbálja újra."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Új eszköz párosítása"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Buildszám"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Buildszám a vágólapra másolva."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 9e746fa..63737d2 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Կարգավորումներ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Խոշորացման պատուհան"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Խոշորացման պատուհանի կառավարման տարրեր"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Մեծացնել"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Փոքրացնել"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Տեղափոխել վերև"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Տեղափոխել ներքև"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Տեղափոխել ձախ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Տեղափոխել աջ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Սարքերի կառավարման տարրեր"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ավելացրեք կառավարման տարրեր ձեր միացված սարքերի համար"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (անջատված է)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Չհաջողվեց միանալ։ Նորից փորձեք։"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Նոր սարքի զուգակցում"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Կառուցման համարը"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Կառուցման համարը պատճենվեց սեղմատախտակին։"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 380e943..da9af02 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setelan"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Jendela Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrol Jendela Pembesaran"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Perbesar"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Perkecil"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pindahkan ke atas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pindahkan ke bawah"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pindahkan ke kiri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pindahkan ke kanan"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrol perangkat"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambahkan kontrol untuk perangkat terhubung"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (terputus)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak dapat terhubung. Coba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sambungkan perangkat baru"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nomor versi"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nomor versi disalin ke papan klip."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index dd12ed6..64ad21f 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Stillingar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Stækkunargluggi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Stækkunarstillingar glugga"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Auka aðdrátt"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Minnka aðdrátt"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Færa upp"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Færa niður"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Færa til vinstri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Færa til hægri"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Tækjastjórnun"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bæta við stýringum fyrir tengd tæki"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (aftengt)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tenging mistókst. Reyndu aftur."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Para nýtt tæki"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Útgáfunúmer smíðar"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Útgáfunúmer smíðar afritað á klippiborð."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 851228f..90f68d9 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Impostazioni"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Finestra ingrandimento"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Finestra controlli di ingrandimento"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumenta lo zoom"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuisci lo zoom"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sposta su"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sposta giù"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sposta a sinistra"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sposta a destra"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Controllo dei dispositivi"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Aggiungi controlli per i dispositivi connessi"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnesso)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Impossibile connettersi. Riprova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Accoppia nuovo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numero build copiato negli appunti."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d4ad45e..14b19c3 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -941,7 +941,7 @@
     <string name="notification_channel_general" msgid="4384774889645929705">"הודעות כלליות"</string>
     <string name="notification_channel_storage" msgid="2720725707628094977">"אחסון"</string>
     <string name="notification_channel_hints" msgid="7703783206000346876">"טיפים"</string>
-    <string name="instant_apps" msgid="8337185853050247304">"אפליקציות אינסטנט"</string>
+    <string name="instant_apps" msgid="8337185853050247304">"אפליקציות ללא התקנה"</string>
     <string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> פועלת"</string>
     <string name="instant_apps_message" msgid="6112428971833011754">"האפליקציה נפתחת בלי התקנה."</string>
     <string name="instant_apps_message_with_help" msgid="1816952263531203932">"האפליקציה נפתחת בלי התקנה. אפשר להקיש כדי לקבל מידע נוסף."</string>
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"הגדרות"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"חלון הגדלה"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"בקרות של חלון ההגדלה"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"התקרבות"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"התרחקות"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"הזזה למעלה"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"הזזה למטה"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"הזזה שמאלה"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"הזזה ימינה"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"פקדי מכשירים"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"יש להוסיף פקדים למכשירים המחוברים"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (מנותק)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"לא ניתן היה להתחבר. יש לנסות שוב."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"התאמה של מכשיר חדש"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"‏מספר Build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"‏מספר ה-Build הועתק ללוח."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 7547853..98e6984 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"拡大ウィンドウ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"拡大ウィンドウ コントロール"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"拡大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"上に移動"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"下に移動"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"左に移動"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"右に移動"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"デバイス コントロール"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"接続済みデバイスのコントロールを追加します"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(未接続)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"接続できませんでした。もう一度お試しください。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"新しいデバイスとのペア設定"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ビルド番号"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ビルド番号をクリップボードにコピーしました。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 69b1d14..0086868 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1012,18 +1012,16 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"პარამეტრები"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"გადიდების ფანჯარა"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"გადიდების კონტროლის ფანჯარა"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
-    <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
-    <skip />
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"მასშტაბის გადიდება"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"მასშტაბის შემცირება"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ზემოთ გადატანა"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ქვემოთ გადატანა"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"მარცხნივ გადატანა"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"მარჯვნივ გადატანა"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"გადიდების გადართვა"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"მთლიანი ეკრანის გადიდება"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"მოწყობილ. მართვის საშუალებები"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"მართვის საშუალებების დამატება თქვენს დაკავშირებულ მოწყობილობებზე"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"მოწყობილობის მართვის საშუალებების დაყენება"</string>
@@ -1087,8 +1085,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (კავშირი გაწყვეტილია)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"დაკავშირება ვერ მოხერხდა. ცადეთ ხელახლა."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ახალი მოწყობილობის დაწყვილება"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ანაწყობის ნომერი"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ანაწყობის ნომერი დაკოპირებულია გაცვლის ბუფერში."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b86f32e..606350c 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Параметрлер"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Ұлғайту терезесі"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ұлғайту терезесінің басқару элементтері"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Ұлғайту"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Кішірейту"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жоғары қарай жылжыту"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмен қарай жылжыту"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солға жылжыту"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңға жылжыту"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Құрылғыны басқару элементтері"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Жалғанған құрылғылар үшін басқару виджеттерін қосу"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылған)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Қосылмады. Қайта қосылып көріңіз."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңа құрылғыны жұптау"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Құрама нөмірі"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Құрама нөмірі буферге көшірілді."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e895140..9ed01c5 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ការកំណត់"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"វិនដូ​ការពង្រីក"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"វិនដូគ្រប់គ្រង​​ការពង្រីក"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ពង្រីក"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"បង្រួម"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ផ្លាស់ទី​ឡើង​លើ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ផ្លាស់ទី​ចុះ​ក្រោម"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ផ្លាស់ទី​ទៅ​ឆ្វេង"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ផ្លាស់ទីទៅ​ស្តាំ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ផ្ទាំងគ្រប់គ្រងឧបករណ៍"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"បញ្ចូល​ផ្ទាំងគ្រប់គ្រង​សម្រាប់​ឧបករណ៍​ដែលអ្នកបានភ្ជាប់"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (បាន​ផ្ដាច់)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"មិន​អាច​ភ្ជាប់​បាន​ទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ផ្គូផ្គង​ឧបករណ៍ថ្មី"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"លេខ​កំណែបង្កើត"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"បានចម្លងលេខ​កំណែបង្កើតទៅឃ្លីបបត។"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2a9f649..116241c 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ವರ್ಧನೆಯ ವಿಂಡೋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ವರ್ಧನೆಯ ವಿಂಡೋ ನಿಯಂತ್ರಣಗಳು"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ಝೂಮ್ ಇನ್ ಮಾಡಿ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ಝೂಮ್ ಔಟ್ ಮಾಡಿ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ಮೇಲೆಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ಕೆಳಗೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ಸಾಧನ ನಿಯಂತ್ರಣಗಳು"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ನಿಮ್ಮ ಸಂಪರ್ಕಿತ ಸಾಧನಗಳಿಗೆ ನಿಯಂತ್ರಣಗಳನ್ನು ಸೇರಿಸಿ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ಬಿಲ್ಡ್ ಸಂಖ್ಯೆಯನ್ನು ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ನಲ್ಲಿ ನಕಲಿಸಲಾಗಿದೆ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 559205c..5bb0d13 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"설정"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"확대 창"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"확대 창 컨트롤"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"확대"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"축소"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"위로 이동"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"아래로 이동"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"왼쪽으로 이동"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"오른쪽으로 이동"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"기기 컨트롤"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"연결된 기기의 컨트롤을 추가하세요."</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(연결 끊김)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"연결할 수 없습니다. 다시 시도하세요."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"새 기기와 페어링"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"빌드 번호"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"빌드 번호가 클립보드에 복사되었습니다."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a688150..dd85504 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Жөндөөлөр"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Чоңойтуу терезеси"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Чоңойтуу терезесин башкаруу каражаттары"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Жакындатуу"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Алыстатуу"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Жогору жылдыруу"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Төмөн жылдыруу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Солго жылдыруу"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Оңго жылдыруу"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Түзмөктү башкаруу элементтери"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Байланышкан түзмөктөрүңүздү башкаруу элементтерин кошосуз"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ажыратылды)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Байланышпай койду. Кайталоо."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Жаңы түзмөктү жупташтыруу"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Курама номери"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Курама номери алмашуу буферине көчүрүлдү."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1a6c338..3553704 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ການຕັ້ງຄ່າ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ໜ້າຈໍການຂະຫຍາຍ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ການຄວບຄຸມໜ້າຈໍການຂະຫຍາຍ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ຊູມເຂົ້າ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ຊູມອອກ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ຍ້າຍຂຶ້ນ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ຍ້າຍລົງ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ຍ້າຍໄປຊ້າຍ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ຍ້າຍໄປຂວາ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ການຄວບຄຸມອຸປະກອນ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ເພີ່ມການຄວບຄຸມສຳລັບອຸປະກອນທີ່ເຊື່ອມຕໍ່ແລ້ວຂອງທ່ານ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ຕັດການເຊື່ອມຕໍ່ແລ້ວ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້. ລອງໃໝ່."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ໝາຍເລກສ້າງ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ສຳເນົາໝາຍເລກສ້າງໄປໃສ່ຄລິບບອດແລ້ວ."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6fb84dd8..9a2a8f2 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1028,6 +1028,14 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Perkelti žemyn"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Perkelti kairėn"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Perkelti dešinėn"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
+    <skip />
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
+    <skip />
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
+    <skip />
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
+    <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Įrenginio valdikliai"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridėkite prijungtų įrenginių valdiklių"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Įrenginio valdiklių nustatymas"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9864a61..94b66f3 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Iestatījumi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Palielināšanas logs"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Palielināšanas loga vadīklas"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Tuvināt"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Tālināt"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Pārvietot uz augšu"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pārvietot uz leju"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Pārvietot pa kreisi"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Pārvietot pa labi"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Ierīču vadīklas"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pievienojiet vadīklas pievienotajām ierīcēm"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (savienojums pārtraukts)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nevarēja izveidot savienojumu. Mēģiniet vēlreiz."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Savienošana pārī ar jaunu ierīci"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versijas numurs"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versijas numurs ir kopēts starpliktuvē."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 362cbdc..567afc0 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Поставки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозорец за зголемување"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроли на прозорец за зголемување"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Зумирај"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Одзумирај"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Премести нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Премести надолу"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Премести налево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Премести надесно"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроли за уредите"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроли за поврзаните уреди"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (исклучен)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не можеше да се поврзе. Обидете се повторно."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Спарете нов уред"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број на верзија"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Бројот на верзијата е копиран во привремената меморија."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 95634b8..dc6fe4c 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ക്രമീകരണം"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"മാഗ്നിഫിക്കേഷൻ വിൻഡോ നിയന്ത്രണങ്ങൾ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"സൂം ഇൻ ചെയ്യുക"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"സൂം ഔട്ട് ചെയ്യുക"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"മുകളിലേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"താഴേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ഇടത്തേക്ക് നീക്കുക"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"വലത്തേക്ക് നീക്കുക"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"കണക്റ്റ് ചെയ്ത ഉപകരണങ്ങൾക്ക് നിയന്ത്രണങ്ങൾ ചേർക്കുക"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (വിച്ഛേദിച്ചു)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"കണക്റ്റ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"പുതിയ ഉപകരണവുമായി ജോടിയാക്കുക"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ബിൽഡ് നമ്പർ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ക്ലിപ്പ്ബോർഡിലേക്ക് ബിൽഡ് നമ്പർ പകർത്തി."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a2e1745..b12c4e0 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Тохиргоо"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Томруулалтын цонх"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Томруулалтын цонхны хяналт"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Томруулах"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Жижигрүүлэх"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Дээш зөөх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Доош зөөх"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Зүүн тийш зөөх"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Баруун тийш зөөх"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Төхөөрөмжийн хяналт"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Холбогдсон төхөөрөмжүүд дээрээ хяналт нэмэх"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (салсан)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Холбогдож чадсангүй. Дахин оролдоно уу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Шинэ төхөөрөмж хослуулах"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Хийгдсэн дугаар"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Хийгдсэн дугаарыг түр санах ойд хуулсан."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 7a62c17..40a51c7 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिंग्ज"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"मॅग्निफिकेशन विंडो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"मॅग्निफिकेशन विंडो नियंत्रणे"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"झूम इन करा"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"झूम आउट करा"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"वर हलवा"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"तुमच्या कनेक्ट केलेल्या डिव्हाइससाठी नियंत्रणे जोडा"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट केले)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट करू शकलो नाही. पुन्हा प्रयत्न करा."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नवीन डिव्हाइससोबत पेअर करा"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नंबर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नंबर क्लिपबोर्डवर कॉपी केला."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 2b501bb..aa302a9 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Tetapan"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Tetingkap Pembesaran"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kawalan Tetingkap Pembesaran"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zum masuk"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zum keluar"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Alih ke atas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Alih ke bawah"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Alih ke kiri"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Alih ke kanan"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kawalan peranti"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Tambah kawalan untuk peranti yang disambungkan"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (diputuskan sambungan)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Tidak boleh menyambung. Cuba lagi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Gandingkan peranti baharu"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nombor binaan"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nombor binaan disalin ke papan keratan."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 1cbf17fe..a296b46 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ဆက်တင်များ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ဝင်းဒိုး ချဲ့ခြင်း"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ဝင်းဒိုး ထိန်းချုပ်မှုများ ချဲ့ခြင်း"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ဇူးမ်ဆွဲရန်"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ဇူးမ်ဖြုတ်ရန်"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"အပေါ်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"အောက်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ဘယ်ဘက်သို့ရွှေ့ရန်"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ညာဘက်သို့ရွှေ့ရန်"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"စက်ထိန်းစနစ်"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ချိတ်ဆက်စက်များအတွက် ထိန်းချုပ်မှုများထည့်ပါ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ချိတ်ဆက်မထားပါ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ချိတ်ဆက်၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"စက်အသစ် တွဲချိတ်ရန်"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"တည်ဆောက်မှုနံပါတ်"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"တည်ဆောက်မှုနံပါတ်ကို ကလစ်ဘုတ်သို့ မိတ္တူကူးပြီးပါပြီ။"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 6d6d66f..7ebed82 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Innstillinger"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Forstørringsvindu"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontroller for forstørringsvindu"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zoom inn"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom ut"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytt opp"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytt ned"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytt til venstre"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytt til høyre"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyring"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Legg til kontroller for de tilkoblede enhetene dine"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frakoblet)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kunne ikke koble til. Prøv på nytt."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Koble til en ny enhet"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delversjonsnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Delversjonsnummeret er kopiert til utklippstavlen."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index e1dd8c0..6b77bb2 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -339,7 +339,7 @@
     <string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
     <string name="start_dreams" msgid="9131802557946276718">"स्क्रिन सेभर"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
-    <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा छोइराख्नुहोस्"</string>
+    <string name="quick_settings_header_onboarding_text" msgid="1918085351115504765">"थप विकल्पहरूका लागि आइकनहरूमा टच एण्ड होल्ड गर्नुहोस्"</string>
     <string name="quick_settings_dnd_label" msgid="7728690179108024338">"बाधा नपुऱ्याउनुहोस्"</string>
     <string name="quick_settings_dnd_priority_label" msgid="6251076422352664571">"प्राथमिकता मात्र"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="1241780970469630835">"अलार्महरू मात्र"</string>
@@ -591,16 +591,16 @@
     <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"असक्षम पार्नुहोस्"</string>
     <string name="accessibility_output_chooser" msgid="7807898688967194183">"आउटपुट यन्त्र बदल्नुहोस्"</string>
     <string name="screen_pinning_title" msgid="9058007390337841305">"एप पिन गरिएको छ"</string>
-    <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
-    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई छोइराख्नुहोस्।"</string>
+    <string name="screen_pinning_description" msgid="8699395373875667743">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न पछाडि र गृह नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"तपाईंले यो एप अनपिन नगरेसम्म यो एप यहाँ देखिइरहने छ। अनपिन गर्न माथितिर स्वाइप गरी होल्ड गर्नुहोस्।"</string>
-    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई छोइराख्नुहोस्।"</string>
-    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई छोइराख्नुहोस्।"</string>
+    <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न परिदृश्य बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
+    <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"तपाईंले अनपिन नगरेसम्म यसले त्यसलाई दृश्यमा कायम राख्छ। अनपिन गर्न गृह नामक बटनलाई टच एण्ड होल्ड गर्नुहोस्।"</string>
     <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"स्क्रिनमा व्यक्तिगत डेटा (जस्तै सम्पर्क ठेगाना र इमेलको सामग्री) देखिन सक्छ।"</string>
     <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"पिन गरिएको एपले अन्य एप खोल्न सक्छ।"</string>
-    <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई छोइराख्नुहोस्"</string>
-    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई छोइराख्नुहोस्"</string>
-    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा छोइराख्नुहोस्"</string>
+    <string name="screen_pinning_toast" msgid="8177286912533744328">"यो एप अनपनि गर्न पछाडि र विवरण नामक बटनहरूलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+    <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"यो एप अनपनि गर्न पछाडि र होम बटनलाई टच एण्ड होल्ड गर्नुहोस्"</string>
+    <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"यो एप अनपिन गर्न माथितिर स्वाइप गरी स्क्रिनमा टच एण्ड होल्ड गर्नुहोस्"</string>
     <string name="screen_pinning_positive" msgid="3285785989665266984">"बुझेँ"</string>
     <string name="screen_pinning_negative" msgid="6882816864569211666">"धन्यवाद पर्दैन"</string>
     <string name="screen_pinning_start" msgid="7483998671383371313">"एप पिन गरियो"</string>
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"सेटिङ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"म्याग्निफिकेसन विन्डो"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"म्याग्निफिकेसन विन्डोका नियन्त्रणहरू"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"जुम इन गर्नुहोस्"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"जुम आउट गर्नुहोस्"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"माथि सार्नुहोस्"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"यन्त्र नियन्त्रण गर्ने विजेटहरू"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"आफ्ना जोडिएका यन्त्रहरूका लागि नियन्त्रण सुविधाहरू थप्नुहोस्"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (डिस्कनेक्ट गरिएको)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"कनेक्ट गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"नयाँ यन्त्रको जोडा बनाउनुहोस्"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"बिल्ड नम्बर"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"बिल्ड नम्बर कपी गरी क्लिपबोर्डमा सारियो।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e2db22c..19e1537 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Instellingen"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Vergrotingsvenster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Bediening van vergrotingsvenster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Inzoomen"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uitzoomen"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Omhoog verplaatsen"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Omlaag verplaatsen"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Naar links verplaatsen"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Naar rechts verplaatsen"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Apparaatbediening"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bedieningselementen voor je gekoppelde apparaten toevoegen"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (verbinding verbroken)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Kan geen verbinding maken. Probeer het nog eens."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Nieuw apparaat koppelen"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Build-nummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build-nummer naar klembord gekopieerd."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 970e61c..d4e723f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ସେଟିଂସ୍"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ମ୍ୟାଗ୍ନିଫିକେସନ୍ ୱିଣ୍ଡୋ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ଜୁମ୍ ଇନ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ଉପରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ତଳକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ଡିଭାଇସ୍ ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ଆପଣଙ୍କ ସଂଯୁକ୍ତ ଡିଭାଇସଗୁଡ଼ିକ ପାଇଁ ନିୟନ୍ତ୍ରଣ ଯୋଗ କରନ୍ତୁ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ସଂଯୋଗ କରାଯାଇପାରିଲା ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ନୂଆ ଡିଭାଇସକୁ ପେୟାର୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ବିଲ୍ଡ ନମ୍ୱର"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"କ୍ଲିପବୋର୍ଡକୁ କପି କରାଯାଇଥିବା ବିଲ୍ଡ ନମ୍ୱର।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 690d6e5b..19715c5 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"ਵੱਡਦਰਸ਼ੀਕਰਨ Window ਦੇ ਕੰਟਰੋਲ"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ਜ਼ੂਮ ਵਧਾਓ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ਜ਼ੂਮ ਘਟਾਓ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ਉੱਪਰ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ਆਪਣੇ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਲਈ ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ਡਿਸਕਨੈਕਟ ਹੋਇਆ)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"ਬਿਲਡ ਨੰਬਰ"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"ਬਿਲਡ ਨੰਬਰ ਨੂੰ ਕਲਿੱਪਬੋਰਡ \'ਤੇ ਕਾਪੀ ਕੀਤਾ ਗਿਆ।"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9b31c52..810ea37 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ustawienia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno powiększenia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Elementy sterujące okna powiększenia"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Powiększ"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomniejsz"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Przesuń w górę"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Przesuń w dół"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Przesuń w lewo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Przesuń w prawo"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Sterowanie urządzeniami"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodaj elementy sterujące połączonymi urządzeniami"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (rozłączono)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nie udało się połączyć. Spróbuj ponownie."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sparuj nowe urządzenie"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numer kompilacji"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numer kompilacji został skopiowany do schowka."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index b76ebda..f8cf881 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 089bbd4..966f15c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Definições"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controlos da janela de ampliação"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Aumentar zoom"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Diminuir zoom"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Mover para cima"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Controlos de dispositivos"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adicione controlos para os dispositivos associados."</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (desligado)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Não foi possível ligar. Tente novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Sincronize o novo dispositivo"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Número da compilação"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número da compilação copiado para a área de transferência."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index b76ebda..f8cf881 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1018,6 +1018,10 @@
     <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover para baixo"</string>
     <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover para a esquerda"</string>
     <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover para a direita"</string>
+    <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Chave de ampliação"</string>
+    <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Ampliar toda a tela"</string>
+    <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
+    <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"Controles do dispositivo"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adiciona controles aos dispositivos conectados"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar controles do dispositivo"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3194b9f..07653f1 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Setări"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Fereastra de mărire"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Comenzi pentru fereastra de mărire"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Măriți"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Micșorați"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Deplasați în sus"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Deplasați în jos"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Deplasați spre stânga"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Deplasați spre dreapta"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Adăugați comenzi pentru dispozitivele conectate"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (s-a deconectat)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nu s-a putut conecta. Reîncercați."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Asociați un nou dispozitiv"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 26e3433..5f9f115 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Настройки"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Окно увеличения"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Настройки окна увеличения"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увеличить"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Уменьшить"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Переместить вверх"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Переместить вниз"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Переместить влево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Переместить вправо"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Управление устройствами"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Добавьте виджеты для управления устройствами."</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (отключено)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не удалось подключиться. Повторите попытку."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Подключить новое устройство"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер сборки"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номер сборки скопирован в буфер обмена."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8eea1ed..64e24be 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"සැකසීම්"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"විශාලන කවුළුව"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"විශාලනය කිරීමේ කවුළු පාලන"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"විශාලනය වැඩි කරන්න"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"විශාලනය අඩු කරන්න"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ඉහළට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"පහළට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"වමට ගෙන යන්න"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"දකුණට ගෙන යන්න"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"උපාංග පාලන"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"ඔබේ සම්බන්ධිත උපාංග සඳහා පාලන එක් කරන්න"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (විසන්ධි විය)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"සම්බන්ධ වීමට නොහැකි විය. නැවත උත්සාහ කරන්න."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"නව උපාංගය යුගල කරන්න"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"නිමැවුම් අංකය"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"නිමැවුම් අංකය පසුරු පුවරුවට පිටපත් කරන ලදි."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 47045d8..bb75f8f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavenia"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Okno priblíženia"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Ovládacie prvky okna priblíženia"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Priblížiť"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Oddialiť"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Posunúť nahor"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Posunúť nadol"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Posunúť doľava"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Posunúť doprava"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Ovládanie zariadení"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Pridajte si ovládače pripojených zariadení"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (odpojené)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepodarilo sa pripojiť. Skúste to znova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Spárovať nové zariadenie"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Číslo zostavy"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Číslo zostavy bolo skopírované do schránky."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 7b68a3a..98abf37 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nastavitve"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Povečevalno okno"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrolniki povečevalnega okna"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Povečaj"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Pomanjšaj"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Premakni navzgor"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Premakni navzdol"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Premakni levo"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Premakni desno"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrolniki naprave"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodajte kontrolnike za povezane naprave"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (povezava prekinjena)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Povezave ni bilo mogoče vzpostaviti. Poskusite znova."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Seznanitev nove naprave"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Delovna različica"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Delovna različica je bila kopirana v odložišče."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b7ebb2a..154d53d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cilësimet"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dritarja e zmadhimit"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kontrollet e dritares së zmadhimit"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zmadho"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zvogëlo"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Lëvize lart"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Shto kontrolle për pajisjet e tua të lidhura"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (e shkëputur)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nuk mund të lidhej. Provo sërish."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Çifto pajisjen e re"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numri i ndërtimit"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Numri i ndërtimit u kopjua te kujtesa e fragmenteve"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index b6a90cd..e59c0bb 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1017,17 +1017,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Подешавања"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Прозор за увећање"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Контроле прозора за увећање"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Увећајте"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Умањите"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Померите нагоре"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Померите надоле"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Померите налево"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Померите надесно"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Контроле уређаја"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додајте контроле за повезане уређаје"</string>
@@ -1093,8 +1095,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (веза је прекинута)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Повезивање није успело. Пробајте поново."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Упари нови уређај"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Број верзије"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Број верзије је копиран у привремену меморију."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 890f504..8213118 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Inställningar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Förstoringsfönster"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Inställningar för förstoringsfönster"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Zooma in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zooma ut"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Flytta uppåt"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Flytta nedåt"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Flytta åt vänster"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Flytta åt höger"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Enhetsstyrning"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lägg till snabbkontroller för anslutna enheter"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (frånkopplad)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Det gick inte att ansluta. Försök igen."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parkoppla en ny enhet"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Versionsnummer"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versionsnumret har kopierats till urklipp."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 34042ad..0a0e91a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mipangilio"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Dirisha la Ukuzaji"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Vidhibiti vya Dirisha la Ukuzaji"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Vuta karibu"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Sogeza mbali"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Sogeza juu"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Sogeza chini"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sogeza kushoto"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sogeza kulia"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Vidhibiti vya vifaa"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Weka vidhibiti vya vifaa ulivyounganisha"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (hakijaunganishwa)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Imeshindwa kuunganisha. Jaribu tena."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Oanisha kifaa kipya"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nambari ya muundo"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nambari ya muundo imewekwa kwenye ubao wa kunakili."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index b30859f..27a9d06 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"அமைப்புகள்"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"பெரிதாக்கல் சாளரம்"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"பெரிதாக்கல் சாளரக் கட்டுப்பாடுகள்"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"பெரிதாக்கு"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"சிறிதாக்கு"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"மேலே நகர்த்து"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"கீழே நகர்த்து"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"இடப்புறம் நகர்த்து"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"வலப்புறம் நகர்த்து"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"சாதனக் கட்டுப்பாடுகள்"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"இணைக்கப்பட்ட சாதனங்களில் கட்டுப்பாடுகளைச் சேர்க்கலாம்"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (இணைப்பு துண்டிக்கப்பட்டது)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"இணைக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"புதிய சாதனத்தை இணைத்தல்"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"பதிப்பு எண்"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"பதிப்பு எண் கிளிப்போர்டுக்கு நகலெடுக்கப்பட்டது."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index cb134a1..5e35a5c 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"సెట్టింగ్‌లు"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"మాగ్నిఫికేషన్ విండో"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"మాగ్నిఫికేషన్ నియంత్రణల విండో"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"దగ్గరగా జూమ్ చేయండి"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"దూరంగా జూమ్ చేయండి"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"పైకి పంపండి"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"కిందకి పంపండి"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ఎడమవైపుగా జరపండి"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"కుడివైపుగా జరపండి"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"పరికరం నియంత్రణలు"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"మీ కనెక్ట్ అయిన పరికరాలకు నియంత్రణలను జోడించండి"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (డిస్‌కనెక్ట్ అయ్యింది)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"కనెక్ట్ చేయడం సాధ్యపడలేదు. మళ్లీ ట్రై చేయండి."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"బిల్డ్ నంబర్"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"బిల్డ్ నంబర్, క్లిప్‌బోర్డ్‌కు కాపీ చేయబడింది."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d678353..3f7e0da 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"การตั้งค่า"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"หน้าต่างการขยาย"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"การควบคุมหน้าต่างการขยาย"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"ซูมเข้า"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"ซูมออก"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"ย้ายขึ้น"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"ระบบควบคุมอุปกรณ์"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"เพิ่มตัวควบคุมของอุปกรณ์ที่เชื่อมต่อ"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (ยกเลิกการเชื่อมต่อแล้ว)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"เชื่อมต่อไม่ได้ ลองใหม่"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"จับคู่อุปกรณ์ใหม่"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"หมายเลขบิวด์"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"คัดลอกหมายเลขบิวด์ไปยังคลิปบอร์ดแล้ว"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 21fdd31..d03f0f8 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Mga Setting"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Window ng Pag-magnify"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Mga Kontrol sa Pag-magnify ng Window"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Mag-zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Mag-zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Itaas"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Ibaba"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Ilipat pakaliwa"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Ilipat pakanan"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Mga kontrol ng device"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Magdagdag ng kontrol para sa mga nakakonektang device"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (nakadiskonekta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Hindi makakonekta. Subukan ulit."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Magpares ng bagong device"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Numero ng build"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nakopya sa clipboard ang numero ng build."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 3e04d19..62f3671 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Ayarlar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Büyütme Penceresi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Büyütme Penceresi Kontrolleri"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yakınlaştır"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzaklaştır"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Yukarı taşı"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Aşağı taşı"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Sola taşı"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Sağa taşı"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Cihaz denetimleri"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Bağlı cihazlarınız için denetimler ekleyin"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (bağlı değil)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Bağlanılamadı. Tekrar deneyin."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yeni cihaz eşle"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Derleme numarası"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Derleme numarası panoya kopyalandı."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index a7c6bef..069577f 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1022,17 +1022,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Налаштування"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Вікно збільшення"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Елементи керування вікна збільшення"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Наблизити"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Віддалити"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Перемістити вгору"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Перемістити вниз"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Перемістити ліворуч"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Перемістити праворуч"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Керування пристроями"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Додайте елементи керування для підключених пристроїв"</string>
@@ -1099,8 +1101,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (відключено)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Не вдалося підключитися. Повторіть спробу."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Підключити новий пристрій"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Номер складання"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Номер складання скопійовано в буфер обміну."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f24492c..ffba2bd 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"ترتیبات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"میگنیفکیشن ونڈو"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"میگنیفکیشن ونڈو کنٹرولز"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"زوم ان کریں"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"زوم آؤٹ کریں"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"اوپر منتقل کریں"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"اپنے منسلک آلات کے لیے کنٹرولز شامل کریں"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (غیر منسلک ہو گیا)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"منسلک نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"نئے آلہ کا جوڑا بنائیں"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"بلڈ نمبر"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"بلڈ نمبر کلپ بورڈ میں کاپی ہو گیا۔"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 32c2406..433eadb 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Sozlamalar"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Kattalashtirish oynasi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Kattalashtirish oynasi sozlamalari"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Yaqinlashtirish"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Uzoqlashtirish"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Tepaga siljitish"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Qurilmalarni boshqarish"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Ulangan qurilmalar uchun boshqaruv elementlari"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (uzilgan)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ulanmadi. Qayta urining."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Yangi qurilmani ulash"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Nashr raqami"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Nashr raqami vaqtinchalik xotiraga nusxalandi."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d9c80df..e337c42 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Cài đặt"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Cửa sổ phóng to"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Các tùy chọn điều khiển cửa sổ phóng to"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Phóng to"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Thu nhỏ"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Di chuyển lên"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Di chuyển xuống"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Di chuyển sang trái"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Di chuyển sang phải"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Điều khiển thiết bị"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Thêm các tùy chọn điều khiển cho các thiết bị đã kết nối của bạn"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (đã ngắt kết nối)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Không thể kết nối. Hãy thử lại."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Ghép nối thiết bị mới"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Số bản dựng"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Đã sao chép số bản dựng vào khay nhớ tạm."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9f9424d..254d974 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"设置"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大窗口"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大窗口控件"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"缩小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"右移"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"设备控制器"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"为您所连接的设备添加控件"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>(已断开连接)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"无法连接。请重试。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"与新设备配对"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本号"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"已将版本号复制到剪贴板。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 2825a9e..e8afc0e 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮細"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"為連接的裝置新增控制選項"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"版本號碼已複製到剪貼簿。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 8acf2c6..0fc7db6 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"設定"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"放大視窗"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"放大視窗控制項"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"放大"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"縮小"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"向上移"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"向下移"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"向左移"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"向右移"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"裝置控制"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"新增已連結裝置的控制項"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (已中斷連線)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"無法連線,請再試一次。"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"配對新裝置"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"版本號碼"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"已將版本號碼複製到剪貼簿。"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4935206..3ada5cc 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1012,17 +1012,19 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Amasethingi"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Iwindi Lesikhulisi"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Izilawuli Zewindi Lesikhulisi"</string>
-    <!-- no translation found for accessibility_control_zoom_in (1189272315480097417) -->
+    <string name="accessibility_control_zoom_in" msgid="1189272315480097417">"Sondeza"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Hlehlisa"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Khuphula"</string>
+    <string name="accessibility_control_move_down" msgid="5390922476900974512">"Yehlisa"</string>
+    <string name="accessibility_control_move_left" msgid="8156206978511401995">"Yisa kwesokunxele"</string>
+    <string name="accessibility_control_move_right" msgid="8926821093629582888">"Yisa kwesokudla"</string>
+    <!-- no translation found for magnification_mode_switch_description (2698364322069934733) -->
     <skip />
-    <!-- no translation found for accessibility_control_zoom_out (69578832020304084) -->
+    <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_up (6622825494014720136) -->
+    <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) -->
     <skip />
-    <!-- no translation found for accessibility_control_move_down (5390922476900974512) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_left (8156206978511401995) -->
-    <skip />
-    <!-- no translation found for accessibility_control_move_right (8926821093629582888) -->
+    <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) -->
     <skip />
     <string name="quick_controls_title" msgid="6839108006171302273">"Izilawuli zezinsiza"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engeza izilawuli zedivayisi yakho exhunyiwe"</string>
@@ -1087,8 +1089,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (inqamukile)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Ayikwazanga ukuxhumeka. Zama futhi."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Bhanqa idivayisi entsha"</string>
-    <!-- no translation found for build_number_clip_data_label (3623176728412560914) -->
-    <skip />
-    <!-- no translation found for build_number_copy_toast (877720921605503046) -->
-    <skip />
+    <string name="build_number_clip_data_label" msgid="3623176728412560914">"Yakha inombolo"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Yakha inombolo ekopishelwe kubhodi yokunamathisela."</string>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6bada51..d1fb6cd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -218,7 +218,7 @@
 
     <!-- Notification ticker displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=30] -->
     <string name="screenshot_saving_ticker">Saving screenshot\u2026</string>
-    <!-- Notification title displayed when a screenshot is being saved to the Gallery. [CHAR LIMIT=50] -->
+    <!-- Informs the user that a screenshot is being saved. [CHAR LIMIT=50] -->
     <string name="screenshot_saving_title">Saving screenshot\u2026</string>
     <!-- Notification title displayed when a screenshot is saved to the Gallery. [CHAR LIMIT=50] -->
     <string name="screenshot_saved_title">Screenshot saved</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index b526a92..b81ffb7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,7 +46,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -82,13 +81,11 @@
 
     private final PackageManager mPackageManager;
     private final BackgroundExecutor mBackgroundExecutor;
-    private final TaskStackChangeListeners mTaskStackChangeListeners;
 
     private ActivityManagerWrapper() {
         final Context context = AppGlobals.getInitialApplication();
         mPackageManager = context.getPackageManager();
         mBackgroundExecutor = BackgroundExecutor.get();
-        mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
     }
 
     public static ActivityManagerWrapper getInstance() {
@@ -360,23 +357,17 @@
     }
 
     /**
-     * Registers a task stack listener with the system.
-     * This should be called on the main thread.
+     * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener}
      */
     public void registerTaskStackListener(TaskStackChangeListener listener) {
-        synchronized (mTaskStackChangeListeners) {
-            mTaskStackChangeListeners.addListener(ActivityManager.getService(), listener);
-        }
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(listener);
     }
 
     /**
-     * Unregisters a task stack listener with the system.
-     * This should be called on the main thread.
+     * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener}
      */
     public void unregisterTaskStackListener(TaskStackChangeListener listener) {
-        synchronized (mTaskStackChangeListeners) {
-            mTaskStackChangeListeners.removeListener(listener);
-        }
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener);
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index f214648..765cd3d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -19,14 +19,12 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityTaskManager;
-import android.app.IActivityManager;
 import android.app.TaskStackListener;
 import android.content.ComponentName;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
 
@@ -42,208 +40,40 @@
 public class TaskStackChangeListeners extends TaskStackListener {
 
     private static final String TAG = TaskStackChangeListeners.class.getSimpleName();
+    private static final TaskStackChangeListeners INSTANCE = new TaskStackChangeListeners();
+
+    private final Impl mImpl;
+
+    private TaskStackChangeListeners() {
+        mImpl = new Impl(Looper.getMainLooper());
+    }
+
+    public static TaskStackChangeListeners getInstance() {
+        return INSTANCE;
+    }
 
     /**
-     * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+     * Registers a task stack listener with the system.
+     * This should be called on the main thread.
      */
-    private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
-    private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
-
-    private final Handler mHandler;
-    private boolean mRegistered;
-
-    public TaskStackChangeListeners(Looper looper) {
-        mHandler = new H(looper);
-    }
-
-    public void addListener(IActivityManager am, TaskStackChangeListener listener) {
-        synchronized (mTaskStackListeners) {
-            mTaskStackListeners.add(listener);
-        }
-        if (!mRegistered) {
-            // Register mTaskStackListener to IActivityManager only once if needed.
-            try {
-                ActivityTaskManager.getService().registerTaskStackListener(this);
-                mRegistered = true;
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to call registerTaskStackListener", e);
-            }
+    public void registerTaskStackListener(TaskStackChangeListener listener) {
+        synchronized (mImpl) {
+            mImpl.addListener(listener);
         }
     }
 
-    public void removeListener(TaskStackChangeListener listener) {
-        boolean isEmpty;
-        synchronized (mTaskStackListeners) {
-            mTaskStackListeners.remove(listener);
-            isEmpty = mTaskStackListeners.isEmpty();
-        }
-        if (isEmpty && mRegistered) {
-            // Unregister mTaskStackListener once we have no more listeners
-            try {
-                ActivityTaskManager.getService().unregisterTaskStackListener(this);
-                mRegistered = false;
-            } catch (Exception e) {
-                Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
-            }
+    /**
+     * Unregisters a task stack listener with the system.
+     * This should be called on the main thread.
+     */
+    public void unregisterTaskStackListener(TaskStackChangeListener listener) {
+        synchronized (mImpl) {
+            mImpl.removeListener(listener);
         }
     }
 
-    @Override
-    public void onTaskStackChanged() throws RemoteException {
-        // Call the task changed callback for the non-ui thread listeners first. Copy to a set of
-        // temp listeners so that we don't lock on mTaskStackListeners while calling all the
-        // callbacks. This call is always on the same binder thread, so we can just synchronize
-        // on the copying of the listener list.
-        synchronized (mTaskStackListeners) {
-            mTmpListeners.addAll(mTaskStackListeners);
-        }
-        for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
-            mTmpListeners.get(i).onTaskStackChangedBackground();
-        }
-        mTmpListeners.clear();
+    private static class Impl extends TaskStackListener implements Handler.Callback {
 
-        mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
-        mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
-    }
-
-    @Override
-    public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
-            throws RemoteException {
-        mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
-        mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
-                new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
-    }
-
-    @Override
-    public void onActivityUnpinned() throws RemoteException {
-        mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
-        mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
-    }
-
-    @Override
-    public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
-            boolean clearedTask, boolean wasVisible) throws RemoteException {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = task;
-        args.argi1 = homeTaskVisible ? 1 : 0;
-        args.argi2 = clearedTask ? 1 : 0;
-        args.argi3 = wasVisible ? 1 : 0;
-        mHandler.removeMessages(H.ON_ACTIVITY_RESTART_ATTEMPT);
-        mHandler.obtainMessage(H.ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
-    }
-
-    @Override
-    public void onActivityForcedResizable(String packageName, int taskId, int reason)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
-                .sendToTarget();
-    }
-
-    @Override
-    public void onActivityDismissingDockedStack() throws RemoteException {
-        mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
-    }
-
-    @Override
-    public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
-            int requestedDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED, requestedDisplayId,
-                0 /* unused */,
-                taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
-            int requestedDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
-                requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onTaskProfileLocked(int taskId, int userId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
-    }
-
-    @Override
-    public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
-    }
-
-    @Override
-    public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
-    }
-
-    @Override
-    public void onTaskRemoved(int taskId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_REMOVED, taskId, 0).sendToTarget();
-    }
-
-    @Override
-    public void onTaskMovedToFront(RunningTaskInfo taskInfo)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException {
-        mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
-                requestedOrientation).sendToTarget();
-    }
-
-    @Override
-    public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken)
-            throws RemoteException {
-        mHandler.obtainMessage(H.ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId, 0 /* unused */,
-                activityToken).sendToTarget();
-    }
-
-    @Override
-    public void onSingleTaskDisplayDrawn(int displayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
-                0 /* unused */).sendToTarget();
-    }
-
-    @Override
-    public void onSingleTaskDisplayEmpty(int displayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
-                0 /* unused */).sendToTarget();
-    }
-
-    @Override
-    public void onTaskDisplayChanged(int taskId, int newDisplayId) throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
-    }
-
-    @Override
-    public void onRecentTaskListUpdated() throws RemoteException {
-        mHandler.obtainMessage(H.ON_TASK_LIST_UPDATED).sendToTarget();
-    }
-
-    @Override
-    public void onRecentTaskListFrozenChanged(boolean frozen) {
-        mHandler.obtainMessage(H.ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
-                .sendToTarget();
-    }
-
-    @Override
-    public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
-        mHandler.obtainMessage(H.ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
-    }
-
-    @Override
-    public void onActivityRotation(int displayId) {
-        mHandler.obtainMessage(H.ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
-                .sendToTarget();
-    }
-
-    private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
         private static final int ON_ACTIVITY_PINNED = 3;
@@ -268,13 +98,205 @@
         private static final int ON_TASK_DESCRIPTION_CHANGED = 24;
         private static final int ON_ACTIVITY_ROTATION = 25;
 
+        /**
+         * List of {@link TaskStackChangeListener} registered from {@link #addListener}.
+         */
+        private final List<TaskStackChangeListener> mTaskStackListeners = new ArrayList<>();
+        private final List<TaskStackChangeListener> mTmpListeners = new ArrayList<>();
 
-        public H(Looper looper) {
-            super(looper);
+        private final Handler mHandler;
+        private boolean mRegistered;
+
+        Impl(Looper looper) {
+            mHandler = new Handler(looper, this);
+        }
+
+        public void addListener(TaskStackChangeListener listener) {
+            synchronized (mTaskStackListeners) {
+                mTaskStackListeners.add(listener);
+            }
+            if (!mRegistered) {
+                // Register mTaskStackListener to IActivityManager only once if needed.
+                try {
+                    ActivityTaskManager.getService().registerTaskStackListener(this);
+                    mRegistered = true;
+                } catch (Exception e) {
+                    Log.w(TAG, "Failed to call registerTaskStackListener", e);
+                }
+            }
+        }
+
+        public void removeListener(TaskStackChangeListener listener) {
+            boolean isEmpty;
+            synchronized (mTaskStackListeners) {
+                mTaskStackListeners.remove(listener);
+                isEmpty = mTaskStackListeners.isEmpty();
+            }
+            if (isEmpty && mRegistered) {
+                // Unregister mTaskStackListener once we have no more listeners
+                try {
+                    ActivityTaskManager.getService().unregisterTaskStackListener(this);
+                    mRegistered = false;
+                } catch (Exception e) {
+                    Log.w(TAG, "Failed to call unregisterTaskStackListener", e);
+                }
+            }
         }
 
         @Override
-        public void handleMessage(Message msg) {
+        public void onTaskStackChanged() {
+            // Call the task changed callback for the non-ui thread listeners first. Copy to a set
+            // of temp listeners so that we don't lock on mTaskStackListeners while calling all the
+            // callbacks. This call is always on the same binder thread, so we can just synchronize
+            // on the copying of the listener list.
+            synchronized (mTaskStackListeners) {
+                mTmpListeners.addAll(mTaskStackListeners);
+            }
+            for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
+                mTmpListeners.get(i).onTaskStackChangedBackground();
+            }
+            mTmpListeners.clear();
+
+            mHandler.removeMessages(ON_TASK_STACK_CHANGED);
+            mHandler.sendEmptyMessage(ON_TASK_STACK_CHANGED);
+        }
+
+        @Override
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+            mHandler.removeMessages(ON_ACTIVITY_PINNED);
+            mHandler.obtainMessage(ON_ACTIVITY_PINNED,
+                    new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
+        }
+
+        @Override
+        public void onActivityUnpinned() {
+            mHandler.removeMessages(ON_ACTIVITY_UNPINNED);
+            mHandler.sendEmptyMessage(ON_ACTIVITY_UNPINNED);
+        }
+
+        @Override
+        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+                boolean clearedTask, boolean wasVisible) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = task;
+            args.argi1 = homeTaskVisible ? 1 : 0;
+            args.argi2 = clearedTask ? 1 : 0;
+            args.argi3 = wasVisible ? 1 : 0;
+            mHandler.removeMessages(ON_ACTIVITY_RESTART_ATTEMPT);
+            mHandler.obtainMessage(ON_ACTIVITY_RESTART_ATTEMPT, args).sendToTarget();
+        }
+
+        @Override
+        public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+            mHandler.obtainMessage(ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onActivityDismissingDockedStack() {
+            mHandler.sendEmptyMessage(ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+        }
+
+        @Override
+        public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo,
+                int requestedDisplayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED,
+                    requestedDisplayId,
+                    0 /* unused */,
+                    taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo,
+                int requestedDisplayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED,
+                    requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onTaskProfileLocked(int taskId, int userId) {
+            mHandler.obtainMessage(ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
+        }
+
+        @Override
+        public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+            mHandler.obtainMessage(ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
+        }
+
+        @Override
+        public void onTaskCreated(int taskId, ComponentName componentName) {
+            mHandler.obtainMessage(ON_TASK_CREATED, taskId, 0, componentName).sendToTarget();
+        }
+
+        @Override
+        public void onTaskRemoved(int taskId) {
+            mHandler.obtainMessage(ON_TASK_REMOVED, taskId, 0).sendToTarget();
+        }
+
+        @Override
+        public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
+            mHandler.obtainMessage(ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+                    requestedOrientation).sendToTarget();
+        }
+
+        @Override
+        public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
+            mHandler.obtainMessage(ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED, displayId,
+                    0 /* unused */,
+                    activityToken).sendToTarget();
+        }
+
+        @Override
+        public void onSingleTaskDisplayDrawn(int displayId) {
+            mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_DRAWN, displayId,
+                    0 /* unused */).sendToTarget();
+        }
+
+        @Override
+        public void onSingleTaskDisplayEmpty(int displayId) {
+            mHandler.obtainMessage(ON_SINGLE_TASK_DISPLAY_EMPTY, displayId,
+                    0 /* unused */).sendToTarget();
+        }
+
+        @Override
+        public void onTaskDisplayChanged(int taskId, int newDisplayId) {
+            mHandler.obtainMessage(ON_TASK_DISPLAY_CHANGED, taskId, newDisplayId).sendToTarget();
+        }
+
+        @Override
+        public void onRecentTaskListUpdated() {
+            mHandler.obtainMessage(ON_TASK_LIST_UPDATED).sendToTarget();
+        }
+
+        @Override
+        public void onRecentTaskListFrozenChanged(boolean frozen) {
+            mHandler.obtainMessage(ON_TASK_LIST_FROZEN_UNFROZEN, frozen ? 1 : 0, 0 /* unused */)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void onTaskDescriptionChanged(RunningTaskInfo taskInfo) {
+            mHandler.obtainMessage(ON_TASK_DESCRIPTION_CHANGED, taskInfo).sendToTarget();
+        }
+
+        @Override
+        public void onActivityRotation(int displayId) {
+            mHandler.obtainMessage(ON_ACTIVITY_ROTATION, displayId, 0 /* unused */)
+                    .sendToTarget();
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
             synchronized (mTaskStackListeners) {
                 switch (msg.what) {
                     case ON_TASK_STACK_CHANGED: {
@@ -298,7 +320,8 @@
                         final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
                             mTaskStackListeners.get(i).onActivityPinned(
-                                    info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
+                                    info.mPackageName, info.mUserId, info.mTaskId,
+                                    info.mStackId);
                         }
                         break;
                     }
@@ -404,8 +427,7 @@
                     }
                     case ON_SINGLE_TASK_DISPLAY_EMPTY: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(
-                                    msg.arg1);
+                            mTaskStackListeners.get(i).onSingleTaskDisplayEmpty(msg.arg1);
                         }
                         break;
                     }
@@ -423,7 +445,8 @@
                     }
                     case ON_TASK_LIST_FROZEN_UNFROZEN: {
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(msg.arg1 != 0);
+                            mTaskStackListeners.get(i).onRecentTaskListFrozenChanged(
+                                    msg.arg1 != 0);
                         }
                         break;
                     }
@@ -445,6 +468,7 @@
             if (msg.obj instanceof SomeArgs) {
                 ((SomeArgs) msg.obj).recycle();
             }
+            return true;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5ad8cad..272954d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -104,6 +104,8 @@
     private boolean mSupportsDarkText;
     private int[] mColorPalette;
 
+    private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+
     public KeyguardClockSwitch(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -128,6 +130,35 @@
         return mClockPlugin != null;
     }
 
+    /**
+      * Update lock screen mode for testing different layouts
+      */
+    public void updateLockScreenMode(int mode) {
+        mLockScreenMode = mode;
+        RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams)
+                mKeyguardStatusArea.getLayoutParams();
+        RelativeLayout.LayoutParams clockLP = (RelativeLayout.LayoutParams)
+                mSmallClockFrame.getLayoutParams();
+
+        if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+            statusAreaLP.removeRule(RelativeLayout.BELOW);
+            statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.clock_view);
+            statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START);
+
+            clockLP.addRule(RelativeLayout.ALIGN_PARENT_END);
+            clockLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+        } else {
+            statusAreaLP.removeRule(RelativeLayout.LEFT_OF);
+            statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
+            statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
+
+            clockLP.removeRule(RelativeLayout.ALIGN_PARENT_END);
+            clockLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        requestLayout();
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -363,6 +394,10 @@
      * these cases.
      */
     void setKeyguardShowingHeader(boolean hasHeader) {
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            hasHeader = false;
+        }
+
         if (mShowingHeader == hasHeader) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 6e11174..9ef2def 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -79,6 +79,11 @@
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
         @Override
+        public void onLockScreenModeChanged(int mode) {
+            updateLockScreenMode(mode);
+        }
+
+        @Override
         public void onTimeChanged() {
             refreshTime();
         }
@@ -255,6 +260,10 @@
         mClockView.refresh();
     }
 
+    private void updateLockScreenMode(int mode) {
+        mClockView.updateLockScreenMode(mode);
+    }
+
     private void updateTimeZone(TimeZone timeZone) {
         mClockView.onTimeZoneChanged(timeZone);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bdb34bb..1a98c20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -100,8 +100,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.util.Assert;
@@ -180,6 +180,10 @@
     private static final int MSG_USER_STOPPED = 340;
     private static final int MSG_USER_REMOVED = 341;
     private static final int MSG_KEYGUARD_GOING_AWAY = 342;
+    private static final int MSG_LOCK_SCREEN_MODE = 343;
+
+    public static final int LOCK_SCREEN_MODE_NORMAL = 0;
+    public static final int LOCK_SCREEN_MODE_LAYOUT_1 = 1;
 
     /** Biometric authentication state: Not listening. */
     private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -263,6 +267,7 @@
     private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
             mCallbacks = Lists.newArrayList();
     private ContentObserver mDeviceProvisionedObserver;
+    private ContentObserver mLockScreenModeObserver;
 
     private boolean mSwitchingUser;
 
@@ -286,6 +291,7 @@
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private final Executor mBackgroundExecutor;
+    private int mLockScreenMode;
 
     /**
      * Short delay before restarting fingerprint authentication after a successful try. This should
@@ -1694,6 +1700,9 @@
                     case MSG_KEYGUARD_GOING_AWAY:
                         handleKeyguardGoingAway((boolean) msg.obj);
                         break;
+                    case MSG_LOCK_SCREEN_MODE:
+                        handleLockScreenMode();
+                        break;
                     default:
                         super.handleMessage(msg);
                         break;
@@ -1796,7 +1805,7 @@
 
         mIsAutomotive = isAutomotive();
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
         mUserManager = context.getSystemService(UserManager.class);
         mIsPrimaryUser = mUserManager.isPrimaryUser();
         int user = ActivityManager.getCurrentUser();
@@ -1828,6 +1837,23 @@
                 }
             }
         }
+
+        updateLockScreenMode();
+        mLockScreenModeObserver = new ContentObserver(mHandler) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateLockScreenMode();
+                mHandler.sendEmptyMessage(MSG_LOCK_SCREEN_MODE);
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.SHOW_NEW_LOCKSCREEN),
+                false, mLockScreenModeObserver);
+    }
+
+    private void updateLockScreenMode() {
+        mLockScreenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_NEW_LOCKSCREEN, 0);
     }
 
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
@@ -2350,6 +2376,20 @@
     }
 
     /**
+     * Handle {@link #MSG_LOCK_SCREEN_MODE}
+     */
+    private void handleLockScreenMode() {
+        Assert.isMainThread();
+        if (DEBUG) Log.d(TAG, "handleLockScreenMode(" + mLockScreenMode + ")");
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onLockScreenModeChanged(mLockScreenMode);
+            }
+        }
+    }
+
+    /**
      * Handle (@line #MSG_TIMEZONE_UPDATE}
      */
     private void handleTimeZoneUpdate(String timeZone) {
@@ -2668,6 +2708,8 @@
         callback.onClockVisibilityChanged();
         callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible);
         callback.onTelephonyCapable(mTelephonyCapable);
+        callback.onLockScreenModeChanged(mLockScreenMode);
+
         for (Entry<Integer, SimData> data : mSimDatas.entrySet()) {
             final SimData state = data.getValue();
             callback.onSimStateChanged(state.subId, state.slotId, state.simState);
@@ -2959,13 +3001,17 @@
             mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver);
         }
 
+        if (mLockScreenModeObserver != null) {
+            mContext.getContentResolver().unregisterContentObserver(mLockScreenModeObserver);
+        }
+
         try {
             ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
         } catch (RemoteException e) {
             Log.d(TAG, "RemoteException onDestroy. cannot unregister userSwitchObserver");
         }
 
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
 
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 12e0ecd..3c5eceb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -317,4 +317,9 @@
      */
     public void onSecondaryLockscreenRequirementChanged(int userId) { }
 
+    /**
+     * Called to switch lock screen layout/clock layouts
+     */
+    public void onLockScreenModeChanged(int mode) { }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index f24644b..9f28e09 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -47,6 +47,7 @@
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -344,6 +345,7 @@
     @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
+    @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory;
 
     @Inject
     public Dependency() {
@@ -541,6 +543,8 @@
 
         mProviders.put(RecordingController.class, mRecordingController::get);
 
+        mProviders.put(MediaOutputDialogFactory.class, mMediaOutputDialogFactory::get);
+
         Dependency.setInstance(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 4657b06..f210d50 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -72,8 +72,6 @@
             Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
             Key.TOUCHED_RINGER_TOGGLE,
             Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP,
-            Key.HAS_SEEN_BUBBLES_EDUCATION,
-            Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION,
             Key.HAS_SEEN_REVERSE_BOTTOM_SHEET,
             Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT,
             Key.HAS_SEEN_PRIORITY_ONBOARDING
@@ -123,8 +121,6 @@
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
         String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
         String HAS_SEEN_ODI_CAPTIONS_TOOLTIP = "HasSeenODICaptionsTooltip";
-        String HAS_SEEN_BUBBLES_EDUCATION = "HasSeenBubblesOnboarding";
-        String HAS_SEEN_BUBBLES_MANAGE_EDUCATION = "HasSeenBubblesManageOnboarding";
         String HAS_SEEN_REVERSE_BOTTOM_SHEET = "HasSeenReverseBottomSheet";
         String CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT = "ControlsStructureSwipeTooltipCount";
         /** Tracks whether the user has seen the onboarding screen for priority conversations */
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 34efa35..02f34ac 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -42,8 +42,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.lang.ref.WeakReference;
@@ -66,11 +66,11 @@
 
     @VisibleForTesting
     @Inject
-    SizeCompatModeActivityController(Context context, ActivityManagerWrapper am,
+    SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
             CommandQueue commandQueue) {
         super(context);
         mCommandQueue = commandQueue;
-        am.registerTaskStackListener(new TaskStackChangeListener() {
+        listeners.registerTaskStackListener(new TaskStackChangeListener() {
             @Override
             public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
                 // Note the callback already runs on main thread.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 1af3897..69a0d65 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -46,9 +46,15 @@
  */
 class MagnificationModeSwitch {
 
-    private static final int DURATION_MS = 5000;
-    private static final int START_DELAY_MS = 3000;
-    private final Runnable mAnimationTask;
+    @VisibleForTesting
+    static final long FADING_ANIMATION_DURATION_MS = 300;
+    private static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000;
+    // The button visible duration starting from the last showButton() called.
+    private int mVisibleDuration = DEFAULT_FADE_OUT_ANIMATION_DELAY_MS;
+    private final Runnable mFadeInAnimationTask;
+    private final Runnable mFadeOutAnimationTask;
+    @VisibleForTesting
+    boolean mIsFadeOutAnimating = false;
 
     private final Context mContext;
     private final WindowManager mWindowManager;
@@ -100,12 +106,19 @@
             }
         });
 
-        mAnimationTask = () -> {
+        mFadeInAnimationTask = () -> {
+            mImageView.animate()
+                    .alpha(1f)
+                    .setDuration(FADING_ANIMATION_DURATION_MS)
+                    .start();
+        };
+        mFadeOutAnimationTask = () -> {
             mImageView.animate()
                     .alpha(0f)
-                    .setDuration(DURATION_MS)
+                    .setDuration(FADING_ANIMATION_DURATION_MS)
                     .withEndAction(() -> removeButton())
                     .start();
+            mIsFadeOutAnimating = true;
         };
     }
 
@@ -128,7 +141,6 @@
         }
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                mImageView.setAlpha(1.0f);
                 mImageView.animate().cancel();
                 mLastDown.set(event.getRawX(), event.getRawY());
                 mLastDrag.set(event.getRawX(), event.getRawY());
@@ -169,9 +181,13 @@
         if (!mIsVisible) {
             return;
         }
-        mImageView.animate().cancel();
-        mWindowManager.removeView(mImageView);
         // Reset button status.
+        mImageView.removeCallbacks(mFadeInAnimationTask);
+        mImageView.removeCallbacks(mFadeOutAnimationTask);
+        mImageView.animate().cancel();
+        mIsFadeOutAnimating = false;
+        mImageView.setAlpha(0f);
+        mWindowManager.removeView(mImageView);
         mIsVisible = false;
         mParams.x = 0;
         mParams.y = 0;
@@ -185,14 +201,15 @@
         if (!mIsVisible) {
             mWindowManager.addView(mImageView, mParams);
             mIsVisible = true;
+            mImageView.postOnAnimation(mFadeInAnimationTask);
         }
-        mImageView.setAlpha(1.0f);
-        // TODO(b/143852371): use accessibility timeout as a delay.
-        // Dismiss the magnification switch button after the button is displayed for a period of
-        // time.
-        mImageView.animate().cancel();
-        mImageView.removeCallbacks(mAnimationTask);
-        mImageView.postDelayed(mAnimationTask, START_DELAY_MS);
+        if (mIsFadeOutAnimating) {
+            mImageView.animate().cancel();
+            mImageView.setAlpha(1f);
+        }
+        // Refresh the time slot of the fade-out task whenever this method is called.
+        mImageView.removeCallbacks(mFadeOutAnimationTask);
+        mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mVisibleDuration);
     }
 
     void onConfigurationChanged(int configDiff) {
@@ -222,6 +239,7 @@
         imageView.setClickable(true);
         imageView.setFocusable(true);
         imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+        imageView.setAlpha(0f);
         return imageView;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 02a672b..c1c2de1 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -47,6 +47,7 @@
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 
 import java.io.PrintWriter;
@@ -171,6 +172,7 @@
     private final DeviceConfigHelper mDeviceConfigHelper;
     private final Lazy<StatusBarStateController> mStatusBarStateController;
     private final Lazy<ActivityManagerWrapper> mActivityManagerWrapper;
+    private final Lazy<TaskStackChangeListeners> mTaskStackChangeListeners;
     private final Lazy<OverviewProxyService> mOverviewProxyService;
     private final Lazy<SysUiState> mSysUiFlagContainer;
     private final Lazy<WakefulnessLifecycle> mWakefulnessLifecycle;
@@ -207,6 +209,7 @@
             DeviceConfigHelper deviceConfigHelper,
             Lazy<StatusBarStateController> statusBarStateController,
             Lazy<ActivityManagerWrapper> activityManagerWrapper,
+            Lazy<TaskStackChangeListeners> taskStackChangeListeners,
             Lazy<OverviewProxyService> overviewProxyService,
             Lazy<SysUiState> sysUiFlagContainer,
             Lazy<WakefulnessLifecycle> wakefulnessLifecycle,
@@ -218,6 +221,7 @@
         mDeviceConfigHelper = deviceConfigHelper;
         mStatusBarStateController = statusBarStateController;
         mActivityManagerWrapper = activityManagerWrapper;
+        mTaskStackChangeListeners = taskStackChangeListeners;
         mOverviewProxyService = overviewProxyService;
         mSysUiFlagContainer = sysUiFlagContainer;
         mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -245,7 +249,7 @@
         ActivityManager.RunningTaskInfo runningTaskInfo =
                 mActivityManagerWrapper.get().getRunningTask();
         mRunningTaskId = runningTaskInfo == null ? 0 : runningTaskInfo.taskId;
-        mActivityManagerWrapper.get().registerTaskStackListener(mTaskStackChangeListener);
+        mTaskStackChangeListeners.get().registerTaskStackListener(mTaskStackChangeListener);
         mOverviewProxyService.get().addCallback(mOverviewProxyListener);
         mSysUiFlagContainer.get().addCallback(mSysUiStateCallback);
         mIsAwake = mWakefulnessLifecycle.get().getWakefulness()
@@ -300,7 +304,7 @@
             mContext = null;
         }
         mStatusBarStateController.get().removeCallback(mStatusBarStateListener);
-        mActivityManagerWrapper.get().unregisterTaskStackListener(mTaskStackChangeListener);
+        mTaskStackChangeListeners.get().unregisterTaskStackListener(mTaskStackChangeListener);
         mOverviewProxyService.get().removeCallback(mOverviewProxyListener);
         mSysUiFlagContainer.get().removeCallback(mSysUiStateCallback);
         mWakefulnessLifecycle.get().removeObserver(mWakefulnessLifecycleObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index 50d559b..61951cc 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -35,6 +35,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 
@@ -82,7 +83,6 @@
         mStatusBarOptionalLazy = statusBarOptionalLazy;
         mStatusBarStateController = Dependency.get(StatusBarStateController.class);
 
-        ActivityManagerWrapper activityManagerWrapper = ActivityManagerWrapper.getInstance();
         mDefaultHome = getCurrentDefaultHome();
         bootCompleteCache.addListener(() -> mDefaultHome = getCurrentDefaultHome());
         IntentFilter intentFilter = new IntentFilter();
@@ -95,12 +95,13 @@
                 mDefaultHome = getCurrentDefaultHome();
             }
         }, intentFilter);
-        mLauncherShowing = isLauncherShowing(activityManagerWrapper.getRunningTask());
-        activityManagerWrapper.registerTaskStackListener(new TaskStackChangeListener() {
-            @Override
-            public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
-                mLauncherShowing = isLauncherShowing(taskInfo);
-            }
+        mLauncherShowing = isLauncherShowing(ActivityManagerWrapper.getInstance().getRunningTask());
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(
+                new TaskStackChangeListener() {
+                    @Override
+                    public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
+                        mLauncherShowing = isLauncherShowing(taskInfo);
+                    }
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index c409d87..e3b0049 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -63,7 +63,7 @@
  * Note that the current architecture is designed so that a single {@link UdfpsController}
  * controls/manages all UDFPS sensors. In other words, a single controller is registered with
  * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
- * as {@link FingerprintManager#onFingerDown(int, int, int, float, float)} or
+ * as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or
  * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
  * {@code sensorId} parameters.
  */
@@ -374,7 +374,7 @@
                 fw.write(mHbmEnableCommand);
                 fw.close();
             }
-            mFingerprintManager.onFingerDown(mUdfpsSensorId, x, y, minor, major);
+            mFingerprintManager.onPointerDown(mUdfpsSensorId, x, y, minor, major);
         } catch (IOException e) {
             mView.hideScrimAndDot();
             Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
@@ -382,7 +382,7 @@
     }
 
     private void onFingerUp() {
-        mFingerprintManager.onFingerUp(mUdfpsSensorId);
+        mFingerprintManager.onPointerUp(mUdfpsSensorId);
         // Hiding the scrim before disabling HBM results in less noticeable flicker.
         mView.hideScrimAndDot();
         if (mHbmSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 529af22..57d8dc7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -15,8 +15,8 @@
  */
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.os.AsyncTask.Status.FINISHED;
-import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
@@ -256,7 +256,8 @@
     }
 
     /**
-     * Cleanup expanded view for bubbles going into overflow.
+     * Call this to clean up the task for the bubble. Ensure this is always called when done with
+     * the bubble.
      */
     void cleanupExpandedView() {
         if (mExpandedView != null) {
@@ -270,8 +271,7 @@
     }
 
     /**
-     * Call when the views should be removed, ensure this is called to clean up ActivityView
-     * content.
+     * Call when all the views should be removed/cleaned up.
      */
     void cleanupViews() {
         cleanupExpandedView();
@@ -468,14 +468,6 @@
         return mIntentActive;
     }
 
-    /**
-     * @return the display id of the virtual display on which bubble contents is drawn.
-     */
-    @Override
-    public int getDisplayId() {
-        return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
-    }
-
     public InstanceId getInstanceId() {
         return mInstanceId;
     }
@@ -490,6 +482,14 @@
     }
 
     /**
+     * @return the task id of the task in which bubble contents is drawn.
+     */
+    @Override
+    public int getTaskId() {
+        return mExpandedView != null ? mExpandedView.getTaskId() : INVALID_TASK_ID;
+    }
+
+    /**
      * Should be invoked whenever a Bubble is accessed (selected while expanded).
      */
     void markAsAccessedAt(long lastAccessedMillis) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2372529..3f94b00 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
@@ -27,8 +28,6 @@
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -46,6 +45,7 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -70,7 +70,6 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseSetArray;
-import android.view.Display;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -80,15 +79,18 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.Dumpable;
 import com.android.systemui.bubbles.dagger.BubbleModule;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -111,6 +113,7 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.pip.PinnedStackListenerForwarder;
@@ -168,8 +171,8 @@
     private final ShadeController mShadeController;
     private final FloatingContentCoordinator mFloatingContentCoordinator;
     private final BubbleDataRepository mDataRepository;
-    private BubbleLogger mLogger = new BubbleLoggerImpl();
-
+    private BubbleLogger mLogger;
+    private final Handler mMainHandler;
     private BubbleData mBubbleData;
     private ScrimView mBubbleScrim;
     @Nullable private BubbleStackView mStackView;
@@ -241,6 +244,8 @@
 
     private boolean mInflateSynchronously;
 
+    private MultiWindowTaskListener mTaskListener;
+
     // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
     private final List<NotifCallback> mCallbacks = new ArrayList<>();
 
@@ -365,20 +370,24 @@
             FeatureFlags featureFlags,
             DumpManager dumpManager,
             FloatingContentCoordinator floatingContentCoordinator,
-            BubbleDataRepository dataRepository,
             SysUiState sysUiState,
             INotificationManager notificationManager,
             @Nullable IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            UiEventLogger uiEventLogger,
+            @Main Handler mainHandler,
+            ShellTaskOrganizer organizer) {
+        BubbleLogger logger = new BubbleLogger(uiEventLogger);
         return new BubbleController(context, notificationShadeWindowController,
-                statusBarStateController, shadeController, new BubbleData(context), synchronizer,
-                configurationController, interruptionStateProvider, zenModeController,
+                statusBarStateController, shadeController, new BubbleData(context, logger),
+                synchronizer, configurationController, interruptionStateProvider, zenModeController,
                 notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
-                dumpManager, floatingContentCoordinator, dataRepository, sysUiState,
-                notificationManager, statusBarService, windowManager, windowManagerShellWrapper,
-                launcherApps);
+                dumpManager, floatingContentCoordinator,
+                new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager,
+                statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger,
+                mainHandler, organizer);
     }
 
     /**
@@ -407,7 +416,10 @@
             @Nullable IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            BubbleLogger bubbleLogger,
+            Handler mainHandler,
+            ShellTaskOrganizer organizer) {
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
@@ -417,6 +429,8 @@
         mFloatingContentCoordinator = floatingContentCoordinator;
         mDataRepository = dataRepository;
         mINotificationManager = notificationManager;
+        mLogger = bubbleLogger;
+        mMainHandler = mainHandler;
         mZenModeController.addCallback(new ZenModeController.Callback() {
             @Override
             public void onZenChanged(int zen) {
@@ -480,7 +494,7 @@
         statusBarStateController.addCallback(mStatusBarStateListener);
 
         mTaskStackListener = new BubbleTaskStackListener();
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
 
         try {
             windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
@@ -515,6 +529,7 @@
                 });
 
         mBubbleIconFactory = new BubbleIconFactory(context);
+        mTaskListener = new MultiWindowTaskListener(mMainHandler, organizer);
 
         launcherApps.registerCallback(new LauncherApps.Callback() {
             @Override
@@ -577,6 +592,12 @@
         }
     }
 
+    private void onBubbleExpandChanged(boolean shouldExpand) {
+        mSysUiState
+                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+                .commitUpdate(mContext.getDisplayId());
+    }
+
     private void setupNEM() {
         mNotificationEntryManager.addNotificationEntryListener(
                 new NotificationEntryListener() {
@@ -783,6 +804,11 @@
         return mBubbleData.getOverflowBubbles();
     }
 
+    @Override
+    public MultiWindowTaskListener getTaskManager() {
+        return mTaskListener;
+    }
+
     /**
      * BubbleStackView is lazily created by this method the first time a Bubble is added. This
      * method initializes the stack view and adds it to the StatusBar just above the scrim.
@@ -791,8 +817,8 @@
         if (mStackView == null) {
             mStackView = new BubbleStackView(
                     mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
-                    mSysUiState, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
-                    this::hideCurrentInputMethod);
+                    this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged,
+                    this::hideCurrentInputMethod, this::onBubbleExpandChanged);
             mStackView.setStackStartPosition(mPositionFromRemovedStack);
             mStackView.addView(mBubbleScrim);
             if (mExpandListener != null) {
@@ -825,9 +851,8 @@
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
-                // Start not focusable - we'll become focusable when expanded so the ActivityView
-                // can use the IME.
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                 PixelFormat.TRANSLUCENT);
 
@@ -843,16 +868,13 @@
             mAddedToWindowManager = true;
             mWindowManager.addView(mStackView, mWmLayoutParams);
         } catch (IllegalStateException e) {
-            // This means the stack has already been added. This shouldn't happen, since we keep
-            // track of that, but just in case, update the previously added view's layout params.
+            // This means the stack has already been added. This shouldn't happen...
             e.printStackTrace();
-            updateWmFlags();
         }
     }
 
     private void onImeVisibilityChanged(boolean imeVisible) {
         mImeVisible = imeVisible;
-        updateWmFlags();
     }
 
     /** Removes the BubbleStackView from the WindowManager if it's there. */
@@ -879,35 +901,6 @@
     }
 
     /**
-     * Updates the BubbleStackView's WindowManager.LayoutParams, and updates the WindowManager with
-     * the new params if the stack has been added.
-     */
-    private void updateWmFlags() {
-        if (mStackView == null) {
-            return;
-        }
-        if (isStackExpanded() && !mImeVisible) {
-            // If we're expanded, and the IME isn't visible, we want to be focusable. This ensures
-            // that any taps within Bubbles (including on the ActivityView) results in Bubbles
-            // receiving focus and clearing it from any other windows that might have it.
-            mWmLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        } else {
-            // If we're collapsed, we don't want to be focusable since tapping on the stack would
-            // steal focus from apps. We also don't want to be focusable if the IME is visible,
-            mWmLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-        }
-
-        if (mAddedToWindowManager) {
-            try {
-                mWindowManager.updateViewLayout(mStackView, mWmLayoutParams);
-            } catch (IllegalArgumentException e) {
-                // If the stack is somehow not there, ignore the attempt to update it.
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
      * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been
      * added in the meantime.
      */
@@ -1015,8 +1008,6 @@
             if (listener != null) {
                 listener.onBubbleExpandChanged(isExpanding, key);
             }
-
-            updateWmFlags();
         });
         if (mStackView != null) {
             mStackView.setExpandListener(mExpandListener);
@@ -1060,7 +1051,7 @@
     @Override
     public boolean isBubbleExpanded(NotificationEntry entry) {
         return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
-                && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey()) ? true : false;
+                && mBubbleData.getSelectedBubble().getKey().equals(entry.getKey());
     }
 
     @Override
@@ -1115,13 +1106,6 @@
         }
     }
 
-    @Override
-    public void performBackPressIfNeeded() {
-        if (mStackView != null) {
-            mStackView.performBackPressIfNeeded();
-        }
-    }
-
     /**
      * Adds or updates a bubble associated with the provided notification entry.
      *
@@ -1602,19 +1586,21 @@
         mStackView.updateContentDescription();
     }
 
-    @Override
-    public int getExpandedDisplayId(Context context) {
+    /**
+     * The task id of the expanded view, if the stack is expanded and not occluded by the
+     * status bar, otherwise returns {@link ActivityTaskManager#INVALID_TASK_ID}.
+     */
+    private int getExpandedTaskId() {
         if (mStackView == null) {
-            return INVALID_DISPLAY;
+            return INVALID_TASK_ID;
         }
-        final boolean defaultDisplay = context.getDisplay() != null
-                && context.getDisplay().getDisplayId() == DEFAULT_DISPLAY;
         final BubbleViewProvider expandedViewProvider = mStackView.getExpandedBubble();
-        if (defaultDisplay && expandedViewProvider != null && isStackExpanded()
+        if (expandedViewProvider != null && isStackExpanded()
+                && !mStackView.isExpansionAnimating()
                 && !mNotificationShadeWindowController.getPanelExpanded()) {
-            return expandedViewProvider.getDisplayId();
+            return expandedViewProvider.getTaskId();
         }
-        return INVALID_DISPLAY;
+        return INVALID_TASK_ID;
     }
 
     @VisibleForTesting
@@ -1648,18 +1634,17 @@
 
         @Override
         public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
-            if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) {
-                if (!mStackView.isExpansionAnimating()) {
-                    mBubbleData.setExpanded(false);
-                }
+            int expandedId = getExpandedTaskId();
+            if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
+                mBubbleData.setExpanded(false);
             }
         }
 
         @Override
-        public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
+        public void onActivityRestartAttempt(RunningTaskInfo taskInfo, boolean homeTaskVisible,
                 boolean clearedTask, boolean wasVisible) {
             for (Bubble b : mBubbleData.getBubbles()) {
-                if (b.getDisplayId() == task.displayId) {
+                if (taskInfo.taskId == b.getTaskId()) {
                     mBubbleData.setSelectedBubble(b);
                     mBubbleData.setExpanded(true);
                     return;
@@ -1667,43 +1652,6 @@
             }
         }
 
-        @Override
-        public void onActivityLaunchOnSecondaryDisplayRerouted() {
-            if (mStackView != null) {
-                mBubbleData.setExpanded(false);
-            }
-        }
-
-        @Override
-        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
-            if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) {
-                if (mImeVisible) {
-                    hideCurrentInputMethod();
-                } else {
-                    mBubbleData.setExpanded(false);
-                }
-            }
-        }
-
-        @Override
-        public void onSingleTaskDisplayDrawn(int displayId) {
-            if (mStackView == null) {
-                return;
-            }
-            mStackView.showExpandedViewContents(displayId);
-        }
-
-        @Override
-        public void onSingleTaskDisplayEmpty(int displayId) {
-            final BubbleViewProvider expandedBubble = mStackView != null
-                    ? mStackView.getExpandedBubble()
-                    : null;
-            int expandedId = expandedBubble != null ? expandedBubble.getDisplayId() : -1;
-            if (mStackView != null && mStackView.isExpanded() && expandedId == displayId) {
-                mBubbleData.setExpanded(false);
-            }
-            mBubbleData.notifyDisplayEmpty(displayId);
-        }
     }
 
     /**
@@ -1747,6 +1695,7 @@
     }
 
     /** PinnedStackListener that dispatches IME visibility updates to the stack. */
+    //TODO(b/170442945): Better way to do this / insets listener?
     private class BubblesImeListener extends PinnedStackListenerForwarder.PinnedStackListener {
         @Override
         public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 55ecb22..b4626f2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -32,10 +32,9 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController.DismissReason;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.SysUiStatsLog;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -53,10 +52,9 @@
 /**
  * Keeps track of active bubbles.
  */
-@SysUISingleton
 public class BubbleData {
 
-    private BubbleLoggerImpl mLogger = new BubbleLoggerImpl();
+    private BubbleLogger mLogger;
 
     private int mCurrentUserId;
 
@@ -155,8 +153,9 @@
      */
     private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
 
-    public BubbleData(Context context) {
+    public BubbleData(Context context, BubbleLogger bubbleLogger) {
         mContext = context;
+        mLogger = bubbleLogger;
         mBubbles = new ArrayList<>();
         mOverflowBubbles = new ArrayList<>();
         mPendingBubbles = new HashMap<>();
@@ -552,22 +551,6 @@
         dispatchPendingChanges();
     }
 
-    /**
-     * Indicates that the provided display is no longer in use and should be cleaned up.
-     *
-     * @param displayId the id of the display to clean up.
-     */
-    void notifyDisplayEmpty(int displayId) {
-        for (Bubble b : mBubbles) {
-            if (b.getDisplayId() == displayId) {
-                if (b.getExpandedView() != null) {
-                    b.getExpandedView().notifyDisplayEmpty();
-                }
-                return;
-            }
-        }
-    }
-
     private void dispatchPendingChanges() {
         if (mListener != null && mStateChange.anythingChanged()) {
             mListener.applyUpdate(mStateChange);
@@ -618,12 +601,12 @@
      * @param normalX Normalized x position of the stack
      * @param normalY Normalized y position of the stack
      */
-     void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
+    void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName,
             int bubbleCount, int bubbleIndex, float normalX, float normalY) {
         if (provider == null) {
             mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY);
         } else if (provider.getKey().equals(BubbleOverflow.KEY)) {
-            if (action == SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
+            if (action == FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED) {
                 mLogger.logShowOverflow(packageName, mCurrentUserId);
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
index f129d31..2ab9e87 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleDataRepository.kt
@@ -17,6 +17,7 @@
 
 import android.annotation.SuppressLint
 import android.annotation.UserIdInt
+import android.content.Context
 import android.content.pm.LauncherApps
 import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
 import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
@@ -26,21 +27,16 @@
 import com.android.systemui.bubbles.storage.BubbleEntity
 import com.android.systemui.bubbles.storage.BubblePersistentRepository
 import com.android.systemui.bubbles.storage.BubbleVolatileRepository
-import com.android.systemui.dagger.SysUISingleton
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.yield
-import javax.inject.Inject
 
-@SysUISingleton
-internal class BubbleDataRepository @Inject constructor(
-    private val volatileRepository: BubbleVolatileRepository,
-    private val persistentRepository: BubblePersistentRepository,
-    private val launcherApps: LauncherApps
-) {
+internal class BubbleDataRepository(context: Context, private val launcherApps: LauncherApps) {
+    private val volatileRepository = BubbleVolatileRepository(launcherApps)
+    private val persistentRepository = BubblePersistentRepository(context)
 
     private val ioScope = CoroutineScope(Dispatchers.IO)
     private val uiScope = CoroutineScope(Dispatchers.Main)
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 83a816b..98a2257 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -16,27 +16,19 @@
 
 package com.android.systemui.bubbles;
 
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
-import static android.graphics.PixelFormat.TRANSPARENT;
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.InsetsState.ITYPE_IME;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.systemui.bubbles.BubbleOverflowActivity.EXTRA_BUBBLE_CONTROLLER;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.ActivityTaskManager;
-import android.app.ActivityView;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -49,18 +41,13 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.ShapeDrawable;
-import android.hardware.display.VirtualDisplay;
-import android.os.Binder;
-import android.os.RemoteException;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.SurfaceControl;
-import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
-import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
@@ -82,18 +69,6 @@
  */
 public class BubbleExpandedView extends LinearLayout {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
-    private static final String WINDOW_TITLE = "ImeInsetsWindowWithoutContent";
-
-    private enum ActivityViewStatus {
-        // ActivityView is being initialized, cannot start an activity yet.
-        INITIALIZING,
-        // ActivityView is initialized, and ready to start an activity.
-        INITIALIZED,
-        // Activity runs in the ActivityView.
-        ACTIVITY_STARTED,
-        // ActivityView is released, so activity launching will no longer be permitted.
-        RELEASED,
-    }
 
     // The triangle pointing to the expanded view
     private View mPointerView;
@@ -101,16 +76,11 @@
     @Nullable private int[] mExpandedViewContainerLocation;
 
     private AlphaOptimizedButton mSettingsIcon;
+    private TaskView mTaskView;
 
-    // Views for expanded state
-    private ActivityView mActivityView;
+    private int mTaskId = INVALID_TASK_ID;
 
-    private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
-    private int mTaskId = -1;
-
-    private PendingIntent mPendingIntent;
-
-    private boolean mKeyboardVisible;
+    private boolean mImeVisible;
     private boolean mNeedsNewHeight;
 
     private Point mDisplaySize;
@@ -121,123 +91,104 @@
     private int mPointerHeight;
     private ShapeDrawable mPointerDrawable;
     private int mExpandedViewPadding;
-
+    private float mCornerRadius = 0f;
 
     @Nullable private Bubble mBubble;
+    private PendingIntent mPendingIntent;
 
     private boolean mIsOverflow;
 
     private Bubbles mBubbles = Dependency.get(Bubbles.class);
     private WindowManager mWindowManager;
-    private ActivityManager mActivityManager;
-
     private BubbleStackView mStackView;
-    private View mVirtualImeView;
-    private WindowManager mVirtualDisplayWindowManager;
-    private boolean mImeShowing = false;
-    private float mCornerRadius = 0f;
 
     /**
      * Container for the ActivityView that has a solid, round-rect background that shows if the
      * ActivityView hasn't loaded.
      */
-    private FrameLayout mActivityViewContainer = new FrameLayout(getContext());
+    private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
 
-    /** The SurfaceView that the ActivityView draws to. */
-    @Nullable private SurfaceView mActivitySurface;
+    private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
+        private boolean mInitialized = false;
+        private boolean mDestroyed = false;
 
-    private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
         @Override
-        public void onActivityViewReady(ActivityView view) {
+        public void onInitialized() {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
+                Log.d(TAG, "onActivityViewReady: destroyed=" + mDestroyed
+                        + " initialized=" + mInitialized
                         + " bubble=" + getBubbleKey());
             }
-            switch (mActivityViewStatus) {
-                case INITIALIZING:
-                case INITIALIZED:
-                    // Custom options so there is no activity transition animation
-                    ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
-                            0 /* enterResId */, 0 /* exitResId */);
-                    options.setTaskAlwaysOnTop(true);
-                    // Soptions.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-                    // Post to keep the lifecycle normal
-                    post(() -> {
-                        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                            Log.d(TAG, "onActivityViewReady: calling startActivity, "
-                                    + "bubble=" + getBubbleKey());
-                        }
-                        if (mActivityView == null) {
-                            mBubbles.removeBubble(getBubbleKey(),
-                                    BubbleController.DISMISS_INVALID_INTENT);
-                            return;
-                        }
-                        try {
-                            if (!mIsOverflow && mBubble.hasMetadataShortcutId()
-                                    && mBubble.getShortcutInfo() != null) {
-                                options.setApplyActivityFlagsForBubbles(true);
-                                mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
-                                        options, null /* sourceBounds */);
-                            } else {
-                                Intent fillInIntent = new Intent();
-                                // Apply flags to make behaviour match documentLaunchMode=always.
-                                fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
-                                fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-                                if (mBubble != null) {
-                                    mBubble.setIntentActive();
-                                }
-                                mActivityView.startActivity(mPendingIntent, fillInIntent, options);
-                            }
-                        } catch (RuntimeException e) {
-                            // If there's a runtime exception here then there's something
-                            // wrong with the intent, we can't really recover / try to populate
-                            // the bubble again so we'll just remove it.
-                            Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
-                                    + ", " + e.getMessage() + "; removing bubble");
-                            mBubbles.removeBubble(getBubbleKey(),
-                                    BubbleController.DISMISS_INVALID_INTENT);
-                        }
-                    });
-                    mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
-                    break;
-                case ACTIVITY_STARTED:
-                    post(() -> mActivityManager.moveTaskToFront(mTaskId, 0));
-                    break;
+
+            if (mDestroyed || mInitialized) {
+                return;
             }
+            // Custom options so there is no activity transition animation
+            ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
+                    0 /* enterResId */, 0 /* exitResId */);
+
+            // TODO: I notice inconsistencies in lifecycle
+            // Post to keep the lifecycle normal
+            post(() -> {
+                if (DEBUG_BUBBLE_EXPANDED_VIEW) {
+                    Log.d(TAG, "onActivityViewReady: calling startActivity, bubble="
+                            + getBubbleKey());
+                }
+                try {
+                    if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+                        mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
+                                options, null /* sourceBounds */);
+                    } else {
+                        Intent fillInIntent = new Intent();
+                        // Apply flags to make behaviour match documentLaunchMode=always.
+                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                        if (mBubble != null) {
+                            mBubble.setIntentActive();
+                        }
+                        mTaskView.startActivity(mPendingIntent, fillInIntent, options);
+                    }
+                } catch (RuntimeException e) {
+                    // If there's a runtime exception here then there's something
+                    // wrong with the intent, we can't really recover / try to populate
+                    // the bubble again so we'll just remove it.
+                    Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
+                            + ", " + e.getMessage() + "; removing bubble");
+                    mBubbles.removeBubble(getBubbleKey(),
+                            BubbleController.DISMISS_INVALID_INTENT);
+                }
+            });
+            mInitialized = true;
         }
 
         @Override
-        public void onActivityViewDestroyed(ActivityView view) {
-            if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-                Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
-                        + " bubble=" + getBubbleKey());
-            }
-            mActivityViewStatus = ActivityViewStatus.RELEASED;
+        public void onReleased() {
+            mDestroyed = true;
         }
 
         @Override
-        public void onTaskCreated(int taskId, ComponentName componentName) {
+        public void onTaskCreated(int taskId, ComponentName name) {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
                 Log.d(TAG, "onTaskCreated: taskId=" + taskId
                         + " bubble=" + getBubbleKey());
             }
-            // Since Bubble ActivityView applies singleTaskDisplay this is
-            // guaranteed to only be called once per ActivityView. The taskId is
-            // saved to use for removeTask, preventing appearance in recent tasks.
+            // The taskId is saved to use for removeTask, preventing appearance in recent tasks.
             mTaskId = taskId;
+
+            // With the task org, the taskAppeared callback will only happen once the task has
+            // already drawn
+            setContentVisibility(true);
         }
 
-        /**
-         * This is only called for tasks on this ActivityView, which is also set to
-         * single-task mode -- meaning never more than one task on this display. If a task
-         * is being removed, it's the top Activity finishing and this bubble should
-         * be removed or collapsed.
-         */
+        @Override
+        public void onTaskVisibilityChanged(int taskId, boolean visible) {
+            setContentVisibility(visible);
+        }
+
         @Override
         public void onTaskRemovalStarted(int taskId) {
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
                 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
-                        + " mActivityViewStatus=" + mActivityViewStatus
                         + " bubble=" + getBubbleKey());
             }
             if (mBubble != null) {
@@ -246,6 +197,13 @@
                         BubbleController.DISMISS_TASK_FINISHED));
             }
         }
+
+        @Override
+        public void onBackPressedOnTaskRoot(int taskId) {
+            if (mTaskId == taskId && mStackView.isExpanded()) {
+                mBubbles.collapseStack();
+            }
+        }
     };
 
     public BubbleExpandedView(Context context) {
@@ -264,7 +222,6 @@
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         updateDimensions();
-        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
     }
 
     void updateDimensions() {
@@ -282,9 +239,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
-        }
 
         Resources res = getResources();
         mPointerView = findViewById(R.id.pointer_view);
@@ -299,35 +253,21 @@
                 R.dimen.bubble_manage_button_height);
         mSettingsIcon = findViewById(R.id.settings_button);
 
-        mActivityView = new ActivityView.Builder(mContext)
-                .setSingleInstance(true)
-                .setDisableSurfaceViewBackgroundLayer(true)
-                .setUseTrustedDisplay(true)
-                .build();
-
+        mTaskView = new TaskView(mContext, mBubbles.getTaskManager());
         // Set ActivityView's alpha value as zero, since there is no view content to be shown.
         setContentVisibility(false);
 
-        mActivityViewContainer.setOutlineProvider(new ViewOutlineProvider() {
+        mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public void getOutline(View view, Outline outline) {
                 outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
             }
         });
-        mActivityViewContainer.setClipToOutline(true);
-        mActivityViewContainer.addView(mActivityView);
-        mActivityViewContainer.setLayoutParams(
+        mExpandedViewContainer.setClipToOutline(true);
+        mExpandedViewContainer.addView(mTaskView);
+        mExpandedViewContainer.setLayoutParams(
                 new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        addView(mActivityViewContainer);
-
-        if (mActivityView != null
-                && mActivityView.getChildCount() > 0
-                && mActivityView.getChildAt(0) instanceof SurfaceView) {
-            // Retrieve the surface from the ActivityView so we can screenshot it and change its
-            // z-ordering. This should always be possible, since ActivityView's constructor adds the
-            // SurfaceView as its first child.
-            mActivitySurface = (SurfaceView) mActivityView.getChildAt(0);
-        }
+        addView(mExpandedViewContainer);
 
         // Expanded stack layout, top to bottom:
         // Expanded view container
@@ -335,33 +275,22 @@
         // ==> expanded view
         //   ==> activity view
         //   ==> manage button
-        bringChildToFront(mActivityView);
+        bringChildToFront(mTaskView);
         bringChildToFront(mSettingsIcon);
+        mTaskView.setListener(mTaskViewListener);
 
         applyThemeAttrs();
 
-        setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
-            // Keep track of IME displaying because we should not make any adjustments that might
-            // cause a config change while the IME is displayed otherwise it'll loose focus.
-            final int keyboardHeight = insets.getSystemWindowInsetBottom()
-                    - insets.getStableInsetBottom();
-            mKeyboardVisible = keyboardHeight != 0;
-            if (!mKeyboardVisible && mNeedsNewHeight) {
-                updateHeight();
-            }
-            return view.onApplyWindowInsets(insets);
-        });
-
         mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
         setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding,
                 mExpandedViewPadding);
         setOnTouchListener((view, motionEvent) -> {
-            if (!usingActivityView()) {
+            if (mTaskView == null) {
                 return false;
             }
 
             final Rect avBounds = new Rect();
-            mActivityView.getBoundsOnScreen(avBounds);
+            mTaskView.getBoundsOnScreen(avBounds);
 
             // Consume and ignore events on the expanded view padding that are within the
             // ActivityView's vertical bounds. These events are part of a back gesture, and so they
@@ -387,51 +316,58 @@
     }
 
     /**
-     * Asks the ActivityView's surface to draw on top of all other views in the window. This is
-     * useful for ordering surfaces during animations, but should otherwise be set to false so that
-     * bubbles and menus can draw over the ActivityView.
+     * Sets whether the surface displaying app content should sit on top. This is useful for
+     * ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble
+     * being dragged out, the manage menu) this is set to false, otherwise it should be true.
      */
     void setSurfaceZOrderedOnTop(boolean onTop) {
-        if (mActivitySurface == null) {
+        if (mTaskView == null) {
             return;
         }
-
-        mActivitySurface.setZOrderedOnTop(onTop, true);
+        mTaskView.setZOrderedOnTop(onTop, true /* allowDynamicChange */);
     }
 
-    /** Return a GraphicBuffer with the contents of the ActivityView's underlying surface. */
+    void setImeVisible(boolean visible) {
+        mImeVisible = visible;
+        if (!mImeVisible && mNeedsNewHeight) {
+            updateHeight();
+        }
+    }
+
+    /** Return a GraphicBuffer with the contents of the task view surface. */
     @Nullable
     SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
-        if (mActivitySurface == null) {
+        if (mTaskView == null) {
             return null;
         }
-
         return SurfaceControl.captureLayers(
-                mActivitySurface.getSurfaceControl(),
-                new Rect(0, 0, mActivityView.getWidth(), mActivityView.getHeight()),
+                mTaskView.getSurfaceControl(),
+                new Rect(0, 0, mTaskView.getWidth(), mTaskView.getHeight()),
                 1 /* scale */);
     }
 
-    int[] getActivityViewLocationOnScreen() {
-        if (mActivityView != null) {
-            return mActivityView.getLocationOnScreen();
+    int[] getTaskViewLocationOnScreen() {
+        if (mTaskView != null) {
+            return mTaskView.getLocationOnScreen();
         } else {
             return new int[]{0, 0};
         }
     }
 
+    // TODO: Could listener be passed when we pass StackView / can we avoid setting this like this
     void setManageClickListener(OnClickListener manageClickListener) {
-        findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
+        mSettingsIcon.setOnClickListener(manageClickListener);
     }
 
     /**
-     * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
-     * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
-     * if a view has been added or removed from on top of the ActivityView, such as the manage menu.
+     * Updates the obscured touchable region for the task surface. This calls onLocationChanged,
+     * which results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is
+     * useful if a view has been added or removed from on top of the ActivityView, such as the
+     * manage menu.
      */
     void updateObscuredTouchableRegion() {
-        if (mActivityView != null) {
-            mActivityView.onLocationChanged();
+        if (mTaskView != null) {
+            mTaskView.onLocationChanged();
         }
     }
 
@@ -440,12 +376,12 @@
                 android.R.attr.dialogCornerRadius,
                 android.R.attr.colorBackgroundFloating});
         mCornerRadius = ta.getDimensionPixelSize(0, 0);
-        mActivityViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
+        mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE));
         ta.recycle();
 
-        if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+        if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
                 mContext.getResources())) {
-            mActivityView.setCornerRadius(mCornerRadius);
+            mTaskView.setCornerRadius(mCornerRadius);
         }
 
         final int mode =
@@ -464,11 +400,8 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mKeyboardVisible = false;
+        mImeVisible = false;
         mNeedsNewHeight = false;
-        if (mActivityView != null) {
-            setImeWindowToDisplay(0, 0);
-        }
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
             Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
         }
@@ -490,84 +423,23 @@
         final float alpha = visibility ? 1f : 0f;
 
         mPointerView.setAlpha(alpha);
-
-        if (mActivityView != null && alpha != mActivityView.getAlpha()) {
-            mActivityView.setAlpha(alpha);
-            mActivityView.bringToFront();
+        if (mTaskView == null) {
+            return;
+        }
+        if (alpha != mTaskView.getAlpha()) {
+            mTaskView.setAlpha(alpha);
         }
     }
 
-    @Nullable ActivityView getActivityView() {
-        return mActivityView;
+    @Nullable
+    View getTaskView() {
+        return mTaskView;
     }
 
     int getTaskId() {
         return mTaskId;
     }
 
-    /**
-     * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
-     * This should be done post-move and post-animation.
-     */
-    void updateInsets(WindowInsets insets) {
-        if (usingActivityView()) {
-            int[] screenLoc = mActivityView.getLocationOnScreen();
-            final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
-            final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
-                    insets.getDisplayCutout() != null
-                            ? insets.getDisplayCutout().getSafeInsetBottom()
-                            : 0);
-            setImeWindowToDisplay(getWidth(), Math.max(activityViewBottom - keyboardTop, 0));
-        }
-    }
-
-    private void setImeWindowToDisplay(int w, int h) {
-        if (getVirtualDisplayId() == INVALID_DISPLAY) {
-            return;
-        }
-        if (h == 0 || w == 0) {
-            if (mImeShowing) {
-                mVirtualImeView.setVisibility(GONE);
-                mImeShowing = false;
-            }
-            return;
-        }
-        final Context virtualDisplayContext = mContext.createDisplayContext(
-                getVirtualDisplay().getDisplay());
-
-        if (mVirtualDisplayWindowManager == null) {
-            mVirtualDisplayWindowManager =
-                    (WindowManager) virtualDisplayContext.getSystemService(Context.WINDOW_SERVICE);
-        }
-        if (mVirtualImeView == null) {
-            mVirtualImeView = new View(virtualDisplayContext);
-            mVirtualImeView.setVisibility(VISIBLE);
-            mVirtualDisplayWindowManager.addView(mVirtualImeView,
-                    getVirtualImeViewAttrs(w, h));
-        } else {
-            mVirtualDisplayWindowManager.updateViewLayout(mVirtualImeView,
-                    getVirtualImeViewAttrs(w, h));
-            mVirtualImeView.setVisibility(VISIBLE);
-        }
-
-        mImeShowing = true;
-    }
-
-    private WindowManager.LayoutParams getVirtualImeViewAttrs(int w, int h) {
-        // To use TYPE_NAVIGATION_BAR_PANEL instead of TYPE_IME_BAR to bypass the IME window type
-        // token check when adding the window.
-        final WindowManager.LayoutParams attrs =
-                new WindowManager.LayoutParams(w, h, TYPE_NAVIGATION_BAR_PANEL,
-                        FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
-                        TRANSPARENT);
-        attrs.gravity = Gravity.BOTTOM;
-        attrs.setTitle(WINDOW_TITLE);
-        attrs.token = new Binder();
-        attrs.providesInsetsTypes = new int[]{ITYPE_IME};
-        attrs.alpha = 0.0f;
-        return attrs;
-    }
-
     void setStackView(BubbleStackView stackView) {
         mStackView = stackView;
     }
@@ -576,7 +448,10 @@
         mIsOverflow = overflow;
 
         Intent target = new Intent(mContext, BubbleOverflowActivity.class);
-        mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
+        Bundle extras = new Bundle();
+        extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mBubbles));
+        target.putExtras(extras);
+        mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
                 target, PendingIntent.FLAG_UPDATE_CURRENT);
         mSettingsIcon.setVisibility(GONE);
     }
@@ -614,7 +489,7 @@
                 mPendingIntent = mBubble.getBubbleIntent();
                 if (mPendingIntent != null || mBubble.hasMetadataShortcutId()) {
                     setContentVisibility(false);
-                    mActivityView.setVisibility(VISIBLE);
+                    mTaskView.setVisibility(VISIBLE);
                 }
             }
             applyThemeAttrs();
@@ -624,59 +499,42 @@
         }
     }
 
+    /**
+     * Bubbles are backed by a pending intent or a shortcut, once the activity is
+     * started we never change it / restart it on notification updates -- unless the bubbles'
+     * backing data switches.
+     *
+     * This indicates if the new bubble is backed by a different data source than what was
+     * previously shown here (e.g. previously a pending intent & now a shortcut).
+     *
+     * @param newBubble the bubble this view is being updated with.
+     * @return true if the backing content has changed.
+     */
     private boolean didBackingContentChange(Bubble newBubble) {
         boolean prevWasIntentBased = mBubble != null && mPendingIntent != null;
         boolean newIsIntentBased = newBubble.getBubbleIntent() != null;
         return prevWasIntentBased != newIsIntentBased;
     }
 
-    /**
-     * Lets activity view know it should be shown / populated with activity content.
-     */
-    void populateExpandedView() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "populateExpandedView: "
-                    + "bubble=" + getBubbleKey());
-        }
-
-        if (usingActivityView()) {
-            mActivityView.setCallback(mStateCallback);
-        } else {
-            Log.e(TAG, "Cannot populate expanded view.");
-        }
-    }
-
-    boolean performBackPressIfNeeded() {
-        if (!usingActivityView()) {
-            return false;
-        }
-        mActivityView.performBackPress();
-        return true;
-    }
-
     void updateHeight() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
-        }
-
         if (mExpandedViewContainerLocation == null) {
             return;
         }
 
-        if (usingActivityView()) {
+        if (mBubble != null || mIsOverflow) {
             float desiredHeight = mOverflowHeight;
             if (!mIsOverflow) {
                 desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
             }
             float height = Math.min(desiredHeight, getMaxExpandedHeight());
-            height = Math.max(height, mMinHeight);
-            ViewGroup.LayoutParams lp = mActivityView.getLayoutParams();
+            height = Math.max(height, mIsOverflow ? mOverflowHeight : mMinHeight);
+            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams();
             mNeedsNewHeight = lp.height != height;
-            if (!mKeyboardVisible) {
-                // If the keyboard is visible... don't adjust the height because that will cause
-                // a configuration change and the keyboard will be lost.
+            if (!mImeVisible) {
+                // If the ime is visible... don't adjust the height because that will cause
+                // a configuration change and the ime will be lost.
                 lp.height = (int) height;
-                mActivityView.setLayoutParams(lp);
+                mTaskView.setLayoutParams(lp);
                 mNeedsNewHeight = false;
             }
             if (DEBUG_BUBBLE_EXPANDED_VIEW) {
@@ -689,12 +547,15 @@
 
     private int getMaxExpandedHeight() {
         mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
+        int expandedContainerY = mExpandedViewContainerLocation != null
+                ? mExpandedViewContainerLocation[1]
+                : 0;
         int bottomInset = getRootWindowInsets() != null
                 ? getRootWindowInsets().getStableInsetBottom()
                 : 0;
 
         return mDisplaySize.y
-                - mExpandedViewContainerLocation[1]
+                - expandedContainerY
                 - getPaddingTop()
                 - getPaddingBottom()
                 - mSettingsIconHeight
@@ -714,14 +575,12 @@
             Log.d(TAG, "updateView: bubble="
                     + getBubbleKey());
         }
-
         mExpandedViewContainerLocation = containerLocationOnScreen;
-
-        if (usingActivityView()
-                && mActivityView.getVisibility() == VISIBLE
-                && mActivityView.isAttachedToWindow()) {
-            mActivityView.onLocationChanged();
+        if (mTaskView != null
+                && mTaskView.getVisibility() == VISIBLE
+                && mTaskView.isAttachedToWindow()) {
             updateHeight();
+            mTaskView.onLocationChanged();
         }
     }
 
@@ -744,65 +603,19 @@
     }
 
     /**
-     * Removes and releases an ActivityView if one was previously created for this bubble.
+     * Cleans up anything related to the task and TaskView.
      */
     public void cleanUpExpandedState() {
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
-                    + ", bubble=" + getBubbleKey());
+            Log.d(TAG, "cleanUpExpandedState: bubble=" + getBubbleKey() + " task=" + mTaskId);
         }
-        if (mActivityView == null) {
-            return;
+        if (mTaskView != null) {
+            mTaskView.release();
         }
-        mActivityView.release();
-        if (mTaskId != -1) {
-            try {
-                ActivityTaskManager.getService().removeTask(mTaskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to remove taskId " + mTaskId);
-            }
-            mTaskId = -1;
+        if (mTaskView != null) {
+            removeView(mTaskView);
+            mTaskView = null;
         }
-        removeView(mActivityView);
-
-        mActivityView = null;
-    }
-
-    /**
-     * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
-     * which {@link ActivityView} uses.
-     */
-    void notifyDisplayEmpty() {
-        if (DEBUG_BUBBLE_EXPANDED_VIEW) {
-            Log.d(TAG, "notifyDisplayEmpty: bubble="
-                    + getBubbleKey()
-                    + " mActivityViewStatus=" + mActivityViewStatus);
-        }
-        if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
-            mActivityViewStatus = ActivityViewStatus.INITIALIZED;
-        }
-    }
-
-    private boolean usingActivityView() {
-        return (mPendingIntent != null || mBubble.hasMetadataShortcutId())
-                && mActivityView != null;
-    }
-
-    /**
-     * @return the display id of the virtual display.
-     */
-    public int getVirtualDisplayId() {
-        if (usingActivityView()) {
-            return mActivityView.getVirtualDisplayId();
-        }
-        return INVALID_DISPLAY;
-    }
-
-    private VirtualDisplay getVirtualDisplay() {
-        if (usingActivityView()) {
-            return mActivityView.getVirtualDisplay();
-        }
-        return null;
     }
 
     /**
@@ -812,7 +625,6 @@
             @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.print("BubbleExpandedView");
         pw.print("  taskId:               "); pw.println(mTaskId);
-        pw.print("  activityViewStatus:   "); pw.println(mActivityViewStatus);
         pw.print("  stackView:            "); pw.println(mStackView);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
index 86ba8c5..48c809d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLogger.java
@@ -19,17 +19,22 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
 
 /**
- * Interface for handling bubble-specific logging.
+ * Implementation of UiEventLogger for logging bubble UI events.
+ *
+ * See UiEventReported atom in atoms.proto for more context.
  */
-public interface BubbleLogger extends UiEventLogger {
+public class BubbleLogger {
+
+    private final UiEventLogger mUiEventLogger;
 
     /**
      * Bubble UI event.
      */
     @VisibleForTesting
-    enum Event implements UiEventLogger.UiEventEnum {
+    public enum Event implements UiEventLogger.UiEventEnum {
 
         @UiEvent(doc = "User dismissed the bubble via gesture, add bubble to overflow.")
         BUBBLE_OVERFLOW_ADD_USER_GESTURE(483),
@@ -70,23 +75,80 @@
         }
     }
 
+    public BubbleLogger(UiEventLogger uiEventLogger) {
+        mUiEventLogger = uiEventLogger;
+    }
+
     /**
      * @param b Bubble involved in this UI event
      * @param e UI event
      */
-    void log(Bubble b, UiEventEnum e);
+    public void log(Bubble b, UiEventLogger.UiEventEnum e) {
+        mUiEventLogger.logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
+    }
 
     /**
-     *
      * @param b Bubble removed from overflow
-     * @param r Reason that bubble was removed from overflow
+     * @param r Reason that bubble was removed
      */
-    void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r);
+    public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
+        if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
+        } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
+        } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
+        } else if (r == BubbleController.DISMISS_BLOCKED) {
+            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
+        }
+    }
 
     /**
-     *
      * @param b Bubble added to overflow
      * @param r Reason that bubble was added to overflow
      */
-    void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r);
-}
+    public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
+        if (r == BubbleController.DISMISS_AGED) {
+            log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
+        } else if (r == BubbleController.DISMISS_USER_GESTURE) {
+            log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
+        }
+    }
+
+    void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
+            float normalY) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+                packageName,
+                null /* notification channel */,
+                0 /* notification ID */,
+                0 /* bubble position */,
+                bubbleCount,
+                action,
+                normalX,
+                normalY,
+                false /* unread bubble */,
+                false /* on-going bubble */,
+                false /* isAppForeground (unused) */);
+    }
+
+    void logShowOverflow(String packageName, int currentUserId) {
+        mUiEventLogger.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
+                packageName);
+    }
+
+    void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
+            float normalX, float normalY, int index) {
+        FrameworkStatsLog.write(FrameworkStatsLog.BUBBLE_UI_CHANGED,
+                packageName,
+                bubble.getChannelId() /* notification channel */,
+                bubble.getNotificationId() /* notification ID */,
+                index,
+                bubbleCount,
+                action,
+                normalX,
+                normalY,
+                bubble.showInShade() /* isUnread */,
+                false /* isOngoing (unused) */,
+                false /* isAppForeground (unused) */);
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
deleted file mode 100644
index ea612af..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import android.os.UserHandle;
-
-import com.android.internal.logging.UiEventLoggerImpl;
-import com.android.systemui.shared.system.SysUiStatsLog;
-
-/**
- * Implementation of UiEventLogger for logging bubble UI events.
- *
- * See UiEventReported atom in atoms.proto for more context.
- */
-public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger {
-
-    /**
-     * @param b Bubble involved in this UI event
-     * @param e UI event
-     */
-    public void log(Bubble b, UiEventEnum e) {
-        logWithInstanceId(e, b.getAppUid(), b.getPackageName(), b.getInstanceId());
-    }
-
-    /**
-     * @param b Bubble removed from overflow
-     * @param r Reason that bubble was removed
-     */
-    public void logOverflowRemove(Bubble b, @BubbleController.DismissReason int r) {
-        if (r == BubbleController.DISMISS_NOTIF_CANCEL) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL);
-        } else if (r == BubbleController.DISMISS_GROUP_CANCELLED) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_GROUP_CANCEL);
-        } else if (r == BubbleController.DISMISS_NO_LONGER_BUBBLE) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_NO_LONGER_BUBBLE);
-        } else if (r == BubbleController.DISMISS_BLOCKED) {
-            log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BLOCKED);
-        }
-    }
-
-    /**
-     * @param b Bubble added to overflow
-     * @param r Reason that bubble was added to overflow
-     */
-    public void logOverflowAdd(Bubble b, @BubbleController.DismissReason int r) {
-        if (r == BubbleController.DISMISS_AGED) {
-            log(b, Event.BUBBLE_OVERFLOW_ADD_AGED);
-        } else if (r == BubbleController.DISMISS_USER_GESTURE) {
-            log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE);
-        }
-    }
-
-    void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX,
-            float normalY) {
-        SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                packageName,
-                null /* notification channel */,
-                0 /* notification ID */,
-                0 /* bubble position */,
-                bubbleCount,
-                action,
-                normalX,
-                normalY,
-                false /* unread bubble */,
-                false /* on-going bubble */,
-                false /* isAppForeground (unused) */);
-    }
-
-    void logShowOverflow(String packageName, int currentUserId) {
-        super.log(BubbleLogger.Event.BUBBLE_OVERFLOW_SELECTED, currentUserId,
-                packageName);
-    }
-
-    void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount,
-            float normalX, float normalY, int index) {
-        SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                packageName,
-                bubble.getChannelId() /* notification channel */,
-                bubble.getNotificationId() /* notification ID */,
-                index,
-                bubbleCount,
-                action,
-                normalX,
-                normalY,
-                bubble.showInShade() /* isUnread */,
-                false /* isOngoing (unused) */,
-                false /* isAppForeground (unused) */);
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index bf7c860..102055d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.bubbles
 
+import android.app.ActivityTaskManager.INVALID_TASK_ID
 import android.content.Context
 import android.content.res.Configuration
 import android.graphics.Bitmap
@@ -37,8 +38,8 @@
     private val stack: BubbleStackView
 ) : BubbleViewProvider {
 
-    private lateinit var bitmap : Bitmap
-    private lateinit var dotPath : Path
+    private lateinit var bitmap: Bitmap
+    private lateinit var dotPath: Path
 
     private var bitmapSize = 0
     private var iconBitmapSize = 0
@@ -167,8 +168,8 @@
         return KEY
     }
 
-    override fun getDisplayId(): Int {
-        return expandedView.virtualDisplayId
+    override fun getTaskId(): Int {
+        return if (expandedView != null) expandedView.getTaskId() else INVALID_TASK_ID
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 5fdda97..fc3f5b6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -22,11 +22,13 @@
 
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -47,13 +49,14 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import javax.inject.Inject;
 
 /**
  * Activity for showing aged out bubbles.
  * Must be public to be accessible to androidx...AppComponentFactory
  */
 public class BubbleOverflowActivity extends Activity {
+    static final String EXTRA_BUBBLE_CONTROLLER = "bubble_controller";
+
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
 
     private LinearLayout mEmptyState;
@@ -93,11 +96,6 @@
         }
     }
 
-    @Inject
-    public BubbleOverflowActivity(Bubbles bubbles) {
-        mBubbles = bubbles;
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -109,6 +107,15 @@
         mEmptyStateSubtitle = findViewById(R.id.bubble_overflow_empty_subtitle);
         mEmptyStateImage = findViewById(R.id.bubble_overflow_empty_state_image);
 
+        Intent intent = getIntent();
+        if (intent != null && intent.getExtras() != null) {
+            IBinder binder = intent.getExtras().getBinder(EXTRA_BUBBLE_CONTROLLER);
+            if (binder instanceof ObjectWrapper) {
+                mBubbles = ((ObjectWrapper<Bubbles>) binder).get();
+            }
+        } else {
+            Log.w(TAG, "Bubble overflow activity created without bubble controller!");
+        }
         updateOverflow();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index e83954b..e2674de 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -27,7 +27,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
-import android.app.ActivityView;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -74,18 +73,13 @@
 import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.systemui.Interpolators;
-import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.animation.AnimatableScaleMatrix;
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
-import com.android.systemui.model.SysUiState;
-import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.util.RelativeTouchListener;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
@@ -121,6 +115,8 @@
     /** Duration of the flyout alpha animations. */
     private static final int FLYOUT_ALPHA_ANIMATION_DURATION = 100;
 
+    private static final int FADE_IN_DURATION = 320;
+
     /** Percent to darken the bubbles when they're in the dismiss target. */
     private static final float DARKEN_PERCENT = 0.3f;
 
@@ -302,7 +298,7 @@
                 pw.println("  expandedViewAlpha:  " + expandedView.getAlpha());
                 pw.println("  expandedViewTaskId: " + expandedView.getTaskId());
 
-                final ActivityView av = expandedView.getActivityView();
+                final View av = expandedView.getTaskView();
 
                 if (av != null) {
                     pw.println("  activityViewVis:    " + av.getVisibility());
@@ -323,8 +319,6 @@
     /** Callback to run when we want to unbubble the given notification's conversation. */
     private Consumer<String> mUnbubbleConversationCallback;
 
-    private SysUiState mSysUiState;
-
     private boolean mViewUpdatedRequested = false;
     private boolean mIsExpansionAnimating = false;
     private boolean mIsBubbleSwitchAnimating = false;
@@ -332,8 +326,6 @@
     /** The view to desaturate/darken when magneted to the dismiss target. */
     @Nullable private View mDesaturateAndDarkenTargetView;
 
-    private LayoutInflater mInflater;
-
     private Rect mTempRect = new Rect();
 
     private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
@@ -401,6 +393,11 @@
     public final Consumer<Boolean> mOnImeVisibilityChanged;
 
     /**
+     * Callback to run when the bubble expand status changes.
+     */
+    private final Consumer<Boolean> mOnBubbleExpandChanged;
+
+    /**
      * Callback to run to ask BubbleController to hide the current IME.
      */
     private final Runnable mHideCurrentInputMethodCallback;
@@ -660,7 +657,7 @@
                                     viewInitialX + dx, velX, velY) <= 0;
                     updateBubbleIcons();
                     logBubbleEvent(null /* no bubble associated with bubble stack move */,
-                            SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
+                            FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
                 }
                 mDismissView.hide();
             }
@@ -742,16 +739,13 @@
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer,
             FloatingContentCoordinator floatingContentCoordinator,
-            SysUiState sysUiState,
             Runnable allBubblesAnimatedOutAction,
             Consumer<Boolean> onImeVisibilityChanged,
-            Runnable hideCurrentInputMethodCallback) {
+            Runnable hideCurrentInputMethodCallback,
+            Consumer<Boolean> onBubbleExpandChanged) {
         super(context);
 
         mBubbleData = data;
-        mInflater = LayoutInflater.from(context);
-
-        mSysUiState = sysUiState;
 
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -865,21 +859,13 @@
 
         mOnImeVisibilityChanged = onImeVisibilityChanged;
         mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
+        mOnBubbleExpandChanged = onBubbleExpandChanged;
 
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
             onImeVisibilityChanged.accept(insets.getInsets(WindowInsets.Type.ime()).bottom > 0);
-
             if (!mIsExpanded || mIsExpansionAnimating) {
                 return view.onApplyWindowInsets(insets);
             }
-            mExpandedAnimationController.updateYPosition(
-                    // Update the insets after we're done translating otherwise position
-                    // calculation for them won't be correct.
-                    () -> {
-                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView().updateInsets(insets);
-                        }
-                    });
             return view.onApplyWindowInsets(insets);
         });
 
@@ -974,7 +960,7 @@
 
         animate()
                 .setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED)
-                .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION);
+                .setDuration(FADE_IN_DURATION);
     }
 
     /**
@@ -1066,7 +1052,7 @@
                         mBubbleData.setExpanded(false);
                         mContext.startActivityAsUser(intent, bubble.getUser());
                         logBubbleEvent(bubble,
-                                SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
+                                FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
                     }
                 });
 
@@ -1082,8 +1068,7 @@
      * Whether the educational view should show for the expanded view "manage" menu.
      */
     private boolean shouldShowManageEdu() {
-        final boolean seen = Prefs.getBoolean(mContext,
-                Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */);
+        final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
         final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
                 && mExpandedBubble != null;
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
@@ -1107,8 +1092,7 @@
      * Whether education view should show for the collapsed stack.
      */
     private boolean shouldShowStackEdu() {
-        final boolean seen = Prefs.getBoolean(getContext(),
-                Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */);
+        final boolean seen = getPrefBoolean(StackEducationViewKt.PREF_STACK_EDUCATION);
         final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext);
         if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
             Log.d(TAG, "Show stack edu: " + shouldShow);
@@ -1116,6 +1100,11 @@
         return shouldShow;
     }
 
+    private boolean getPrefBoolean(String key) {
+        return mContext.getSharedPreferences(mContext.getPackageName(), Context.MODE_PRIVATE)
+                .getBoolean(key, false /* default */);
+    }
+
     /**
      * @return true if education view for collapsed stack should show and was not showing before.
      */
@@ -1254,7 +1243,15 @@
 
         mTempRect.setEmpty();
         getTouchableRegion(mTempRect);
-        inoutInfo.touchableRegion.set(mTempRect);
+        if (mIsExpanded && mExpandedBubble != null
+                && mExpandedBubble.getExpandedView() != null
+                && mExpandedBubble.getExpandedView().getTaskView() != null) {
+            inoutInfo.touchableRegion.set(mTempRect);
+            mExpandedBubble.getExpandedView().getTaskView().getBoundsOnScreen(mTempRect);
+            inoutInfo.touchableRegion.op(mTempRect, Region.Op.DIFFERENCE);
+        } else {
+            inoutInfo.touchableRegion.set(mTempRect);
+        }
     }
 
     @Override
@@ -1442,13 +1439,6 @@
     }
 
     /**
-     * The {@link BadgedImageView} that is expanded, null if one does not exist.
-     */
-    View getExpandedBubbleView() {
-        return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
-    }
-
-    /**
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
     @Nullable
@@ -1488,7 +1478,7 @@
                 new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
         animateInFlyoutForBubble(bubble);
         requestUpdate();
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
     }
 
     // via BubbleData.Listener
@@ -1508,7 +1498,7 @@
                     bubble.cleanupViews();
                 }
                 updatePointerPosition();
-                logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
+                logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
             }
         }
@@ -1526,7 +1516,7 @@
     void updateBubble(Bubble bubble) {
         animateInFlyoutForBubble(bubble);
         requestUpdate();
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__UPDATED);
     }
 
     public void updateBubbleOrder(List<Bubble> bubbles) {
@@ -1563,7 +1553,7 @@
             return;
         }
 
-        if (bubbleToSelect.getKey() == BubbleOverflow.KEY) {
+        if (bubbleToSelect.getKey().equals(BubbleOverflow.KEY)) {
             mBubbleData.setShowingOverflow(true);
         } else {
             mBubbleData.setShowingOverflow(false);
@@ -1622,8 +1612,9 @@
                 requestUpdate();
 
                 logBubbleEvent(previouslySelected,
-                        SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
-                logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+                logBubbleEvent(bubbleToSelect,
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
                 notifyExpansionChanged(previouslySelected, false /* expanded */);
                 notifyExpansionChanged(bubbleToSelect, true /* expanded */);
             });
@@ -1653,30 +1644,21 @@
 
         hideCurrentInputMethod();
 
-        mSysUiState
-                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
-                .commitUpdate(mContext.getDisplayId());
+        mOnBubbleExpandChanged.accept(shouldExpand);
 
         if (mIsExpanded) {
             animateCollapse();
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
         } else {
             animateExpansion();
             // TODO: move next line to BubbleData
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
-            logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+            logBubbleEvent(mExpandedBubble,
+                    FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
         }
         notifyExpansionChanged(mExpandedBubble, mIsExpanded);
     }
 
-    void showExpandedViewContents(int displayId) {
-        if (mExpandedBubble != null
-                && mExpandedBubble.getExpandedView() != null
-                && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
-            mExpandedBubble.setContentVisibility(true);
-        }
-    }
-
     /**
      * Asks the BubbleController to hide the IME from anywhere, whether it's focused on Bubbles or
      * not.
@@ -1711,7 +1693,6 @@
         updateOverflowVisibility();
         updatePointerPosition();
         mExpandedAnimationController.expandFromStack(() -> {
-            afterExpandedViewAnimation();
             if (mIsExpanded && mExpandedBubble.getExpandedView() != null) {
                 maybeShowManageEdu();
             }
@@ -1774,11 +1755,10 @@
                                 mExpandedViewContainerMatrix);
                     })
                     .withEndActions(() -> {
+                        afterExpandedViewAnimation();
                         if (mExpandedBubble != null
                                 && mExpandedBubble.getExpandedView() != null) {
                             mExpandedBubble.getExpandedView()
-                                    .setContentVisibility(true);
-                            mExpandedBubble.getExpandedView()
                                     .setSurfaceZOrderedOnTop(false);
                         }
                     })
@@ -1922,7 +1902,6 @@
                     })
                     .withEndActions(() -> {
                         if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView().setContentVisibility(true);
                             mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
                         }
 
@@ -1958,13 +1937,6 @@
         }
     }
 
-    /** Return the BubbleView at the given index from the bubble container. */
-    public BadgedImageView getBubbleAt(int i) {
-        return getBubbleCount() > i
-                ? (BadgedImageView) mBubbleContainer.getChildAt(i)
-                : null;
-    }
-
     /** Moves the bubbles out of the way if they're going to be over the keyboard. */
     public void onImeVisibilityChanged(boolean visible, int height) {
         mStackAnimationController.setImeHeight(visible ? height + mImeOffset : 0);
@@ -1986,6 +1958,9 @@
                                 FLYOUT_IME_ANIMATION_SPRING_CONFIG)
                         .start();
             }
+        } else if (mIsExpanded && mExpandedBubble != null
+                && mExpandedBubble.getExpandedView() != null) {
+            mExpandedBubble.getExpandedView().setImeVisible(visible);
         }
     }
 
@@ -2285,7 +2260,7 @@
         });
         mFlyout.removeCallbacks(mHideFlyout);
         mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
-        logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
+        logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
     }
 
     /** Hide the flyout immediately and cancel any pending hide runnables. */
@@ -2393,7 +2368,6 @@
         final float targetY = mTempRect.bottom - mManageMenu.getHeight();
 
         final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
-
         if (show) {
             mManageMenu.setScaleX(0.5f);
             mManageMenu.setScaleY(0.5f);
@@ -2410,6 +2384,8 @@
                     .withEndActions(() -> {
                         View child = mManageMenu.getChildAt(0);
                         child.requestAccessibilityFocus();
+                        // Update the AV's obscured touchable region for the new visibility state.
+                        mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
                     })
                     .start();
 
@@ -2421,12 +2397,15 @@
                     .spring(DynamicAnimation.SCALE_Y, 0.5f)
                     .spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
                     .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
-                    .withEndActions(() -> mManageMenu.setVisibility(View.INVISIBLE))
+                    .withEndActions(() -> {
+                        mManageMenu.setVisibility(View.INVISIBLE);
+                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                            // Update the AV's obscured touchable region for the new state.
+                            mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+                        }
+                    })
                     .start();
         }
-
-        // Update the AV's obscured touchable region for the new menu visibility state.
-        mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
     }
 
     private void updateExpandedBubble() {
@@ -2446,7 +2425,6 @@
             mExpandedViewContainer.setAlpha(0f);
             mExpandedViewContainer.addView(bev);
             bev.setManageClickListener((view) -> showManageMenu(!mShowingManage));
-            bev.populateExpandedView();
 
             if (!mIsExpansionAnimating) {
                 mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
@@ -2503,7 +2481,7 @@
         mAnimatingOutSurfaceContainer.setTranslationY(0);
 
         final int[] activityViewLocation =
-                mExpandedBubble.getExpandedView().getActivityViewLocationOnScreen();
+                mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen();
         final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
 
         // Translate the surface to overlap the real ActivityView.
@@ -2679,17 +2657,6 @@
                 getNormalizedYPosition());
     }
 
-    /**
-     * Called when a back gesture should be directed to the Bubbles stack. When expanded,
-     * a back key down/up event pair is forwarded to the bubble Activity.
-     */
-    boolean performBackPressIfNeeded() {
-        if (!isExpanded() || mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
-            return false;
-        }
-        return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
-    }
-
     /** For debugging only */
     List<Bubble> getBubblesOnScreen() {
         List<Bubble> bubbles = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
deleted file mode 100644
index 06205c5..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTaskView.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.window.TaskEmbedder;
-import android.window.TaskOrganizerTaskEmbedder;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.view.IWindow;
-import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-
-import dalvik.system.CloseGuard;
-
-
-public class BubbleTaskView extends SurfaceView implements SurfaceHolder.Callback,
-        TaskEmbedder.Host {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleTaskView" : TAG_BUBBLES;
-
-    private final CloseGuard mGuard = CloseGuard.get();
-    private boolean mOpened; // Protected by mGuard.
-
-    private TaskEmbedder mTaskEmbedder;
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
-    private final Rect mTmpRect = new Rect();
-
-    public BubbleTaskView(Context context) {
-        super(context);
-
-        mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this);
-        setUseAlpha();
-        getHolder().addCallback(this);
-
-        mOpened = true;
-        mGuard.open("release");
-    }
-
-    public void setCallback(TaskEmbedder.Listener callback) {
-        if (callback == null) {
-            mTaskEmbedder.setListener(null);
-            return;
-        }
-        mTaskEmbedder.setListener(callback);
-    }
-
-    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
-            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
-        mTaskEmbedder.startShortcutActivity(shortcut, options, sourceBounds);
-    }
-
-    public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
-            @NonNull ActivityOptions options) {
-        mTaskEmbedder.startActivity(pendingIntent, fillInIntent, options);
-    }
-
-    public void onLocationChanged() {
-        mTaskEmbedder.notifyBoundsChanged();
-    }
-
-    @Override
-    public Rect getScreenBounds() {
-        getBoundsOnScreen(mTmpRect);
-        return mTmpRect;
-    }
-
-    @Override
-    public void onTaskBackgroundColorChanged(TaskEmbedder ts, int bgColor) {
-        setResizeBackgroundColor(bgColor);
-    }
-
-    @Override
-    public Region getTapExcludeRegion() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public Matrix getScreenToTaskMatrix() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public IWindow getWindow() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public Point getPositionInWindow() {
-        // Not used
-        return null;
-    }
-
-    @Override
-    public boolean canReceivePointerEvents() {
-        // Not used
-        return false;
-    }
-
-    public void release() {
-        if (!mTaskEmbedder.isInitialized()) {
-            throw new IllegalStateException(
-                    "Trying to release container that is not initialized.");
-        }
-        performRelease();
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            if (mGuard != null) {
-                mGuard.warnIfOpen();
-                performRelease();
-            }
-        } finally {
-            super.finalize();
-        }
-    }
-
-    private void performRelease() {
-        if (!mOpened) {
-            return;
-        }
-        getHolder().removeCallback(this);
-        mTaskEmbedder.release();
-        mTaskEmbedder.setListener(null);
-
-        mGuard.close();
-        mOpened = false;
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        if (!mTaskEmbedder.isInitialized()) {
-            mTaskEmbedder.initialize(getSurfaceControl());
-        } else {
-            mTmpTransaction.reparent(mTaskEmbedder.getSurfaceControl(),
-                    getSurfaceControl()).apply();
-        }
-        mTaskEmbedder.start();
-    }
-
-    @Override
-    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-        mTaskEmbedder.resizeTask(width, height);
-        mTaskEmbedder.notifyBoundsChanged();
-    }
-
-    @Override
-    public void surfaceDestroyed(SurfaceHolder holder) {
-        mTaskEmbedder.stop();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index 916ad18..5cc24ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -48,5 +48,5 @@
 
     boolean showDot();
 
-    int getDisplayId();
+    int getTaskId();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
index 34828b3..39c750d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java
@@ -17,8 +17,6 @@
 package com.android.systemui.bubbles;
 
 import android.annotation.NonNull;
-import android.content.Context;
-import android.view.Display;
 
 import androidx.annotation.MainThread;
 
@@ -57,12 +55,6 @@
      */
     ScrimView getScrimForBubble();
 
-    /**
-     * @return the display id of the expanded view, if the stack is expanded and not occluded by the
-     * status bar, otherwise returns {@link Display#INVALID_DISPLAY}.
-     */
-    int getExpandedDisplayId(Context context);
-
     /** @return Bubbles for updating overflow. */
     List<Bubble> getOverflowBubbles();
 
@@ -77,13 +69,6 @@
      */
     void expandStackAndSelectBubble(NotificationEntry entry);
 
-
-    /**
-     * Directs a back gesture at the bubble stack. When opened, the current expanded bubble
-     * is forwarded a back key down/up pair.
-     */
-    void performBackPressIfNeeded();
-
     /** Promote the provided bubbles when overflow view. */
     void promoteBubbleFromOverflow(Bubble bubble);
 
@@ -142,4 +127,7 @@
 
     /** Set a listener to be notified of when overflow view update. */
     void setOverflowListener(BubbleData.Listener listener);
+
+    /** The task listener for events in bubble tasks. **/
+    MultiWindowTaskListener getTaskManager();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
index 26a9773..3db07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -25,8 +25,6 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION
 import com.android.systemui.R
 
 /**
@@ -38,8 +36,8 @@
     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
 
-    private val ANIMATE_DURATION : Long = 200
-    private val ANIMATE_DURATION_SHORT : Long = 40
+    private val ANIMATE_DURATION: Long = 200
+    private val ANIMATE_DURATION_SHORT: Long = 40
 
     private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
     private val manageButton by lazy { findViewById<Button>(R.id.manage) }
@@ -50,7 +48,7 @@
     private var isHiding = false
 
     init {
-        LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this);
+        LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
 
@@ -95,7 +93,7 @@
      *
      * @param show whether the user education view should show or not.
      */
-    fun show(expandedView: BubbleExpandedView, rect : Rect) {
+    fun show(expandedView: BubbleExpandedView, rect: Rect) {
         if (visibility == VISIBLE) return
 
         alpha = 0f
@@ -136,10 +134,13 @@
             .withEndAction {
                 isHiding = false
                 visibility = GONE
-            };
+            }
     }
 
     private fun setShouldShow(shouldShow: Boolean) {
-        Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow)
+        context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply()
     }
-}
\ No newline at end of file
+}
+
+const val PREF_MANAGED_EDUCATION: String = "HasSeenBubblesManageOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
new file mode 100644
index 0000000..dff8bec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java
@@ -0,0 +1,177 @@
+/*
+ * 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.bubbles;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityTaskManager;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.SurfaceControl;
+import android.window.TaskOrganizer;
+import android.window.WindowContainerToken;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+
+/**
+ * Manages tasks that are displayed in multi-window (e.g. bubbles). These are displayed in a
+ * {@link TaskView}.
+ *
+ * This class listens on {@link TaskOrganizer} callbacks for events. Once visible, these tasks will
+ * intercept back press events.
+ *
+ * @see android.app.WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW
+ * @see TaskView
+ */
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class MultiWindowTaskListener implements ShellTaskOrganizer.TaskListener {
+    private static final String TAG = MultiWindowTaskListener.class.getSimpleName();
+
+    private static final boolean DEBUG = false;
+
+    //TODO(b/170153209): Have shell listener allow per task registration and remove this.
+    public interface Listener {
+        void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash);
+        void onTaskVanished(RunningTaskInfo taskInfo);
+        void onTaskInfoChanged(RunningTaskInfo taskInfo);
+        void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo);
+    }
+
+    private static class TaskData {
+        final RunningTaskInfo taskInfo;
+        final Listener listener;
+
+        TaskData(RunningTaskInfo info, Listener l) {
+            taskInfo = info;
+            listener = l;
+        }
+    }
+
+    private final Handler mHandler;
+    private final ShellTaskOrganizer mTaskOrganizer;
+    private final ArrayMap<WindowContainerToken, TaskData> mTasks = new ArrayMap<>();
+
+    private MultiWindowTaskListener.Listener mPendingListener;
+
+    /**
+     * Create a listener for tasks in multi-window mode.
+     */
+    public MultiWindowTaskListener(Handler handler, ShellTaskOrganizer organizer) {
+        mHandler = handler;
+        mTaskOrganizer = organizer;
+        mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_MULTI_WINDOW);
+    }
+
+    /**
+     * @return the task organizer that is listened to.
+     */
+    public TaskOrganizer getTaskOrganizer() {
+        return mTaskOrganizer;
+    }
+
+    // TODO(b/129067201): track launches for bubbles
+    // Once we have key in ActivityOptions, match listeners via that key
+    public void setPendingListener(Listener listener) {
+        mPendingListener = listener;
+    }
+
+    /**
+     * Removes a task listener previously registered when starting a new activity.
+     */
+    public void removeListener(Listener listener) {
+        if (DEBUG) {
+            Log.d(TAG, "removeListener: listener=" + listener);
+        }
+        if (mPendingListener == listener) {
+            mPendingListener = null;
+        }
+        for (int i = 0; i < mTasks.size(); i++) {
+            if (mTasks.valueAt(i).listener == listener) {
+                mTasks.removeAt(i);
+            }
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+        if (DEBUG) {
+            Log.d(TAG, "onTaskAppeared: taskInfo=" + taskInfo
+                    + " mPendingListener=" + mPendingListener);
+        }
+        if (mPendingListener == null) {
+            // If there is no pending listener, then we are either receiving this task as a part of
+            // registering the task org again (ie. after SysUI dies) or the previously started
+            // task is no longer needed (ie. bubble is closed soon after), for now, just finish the
+            // associated task
+            try {
+                ActivityTaskManager.getService().removeTask(taskInfo.taskId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to remove taskId " + taskInfo.taskId);
+            }
+            return;
+        }
+
+        mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, true);
+
+        final TaskData data = new TaskData(taskInfo, mPendingListener);
+        mTasks.put(taskInfo.token, data);
+        mHandler.post(() -> data.listener.onTaskAppeared(taskInfo, leash));
+        mPendingListener = null;
+    }
+
+    @Override
+    public void onTaskVanished(RunningTaskInfo taskInfo) {
+        final TaskData data = mTasks.remove(taskInfo.token);
+        if (data == null) {
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "onTaskVanished: taskInfo=" + taskInfo + " listener=" + data.listener);
+        }
+        mHandler.post(() -> data.listener.onTaskVanished(taskInfo));
+    }
+
+    @Override
+    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+        final TaskData data = mTasks.get(taskInfo.token);
+        if (data == null) {
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
+        }
+        mHandler.post(() -> data.listener.onTaskInfoChanged(taskInfo));
+    }
+
+    @Override
+    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+        final TaskData data = mTasks.get(taskInfo.token);
+        if (data == null) {
+            return;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener);
+        }
+        mHandler.post(() -> data.listener.onBackPressedOnTaskRoot(taskInfo));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java b/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
new file mode 100644
index 0000000..f054122
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ObjectWrapper.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bubbles;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+// Copied from Launcher3
+/**
+ * Utility class to pass non-parcealable objects within same process using parcealable payload.
+ *
+ * It wraps the object in a binder as binders are singleton within a process
+ */
+public class ObjectWrapper<T> extends Binder {
+
+    private T mObject;
+
+    public ObjectWrapper(T object) {
+        mObject = object;
+    }
+
+    public T get() {
+        return mObject;
+    }
+
+    public void clear() {
+        mObject = null;
+    }
+
+    public static IBinder wrap(Object obj) {
+        return new ObjectWrapper<>(obj);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
rename to packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
index 8880df9..b1291a5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/RelativeTouchListener.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.util
+package com.android.systemui.bubbles
 
 import android.graphics.PointF
 import android.os.Handler
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
index 3e4c729..9e7a2fb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt
@@ -24,21 +24,19 @@
 import android.widget.TextView
 import com.android.internal.util.ContrastColorUtil
 import com.android.systemui.Interpolators
-import com.android.systemui.Prefs
-import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION
 import com.android.systemui.R
 
 /**
  * User education view to highlight the collapsed stack of bubbles.
  * Shown only the first time a user taps the stack.
  */
-class StackEducationView constructor(context: Context) : LinearLayout(context){
+class StackEducationView constructor(context: Context) : LinearLayout(context) {
 
     private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView"
         else BubbleDebugConfig.TAG_BUBBLES
 
-    private val ANIMATE_DURATION : Long = 200
-    private val ANIMATE_DURATION_SHORT : Long = 40
+    private val ANIMATE_DURATION: Long = 200
+    private val ANIMATE_DURATION_SHORT: Long = 40
 
     private val view by lazy { findViewById<View>(R.id.stack_education_layout) }
     private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) }
@@ -47,7 +45,7 @@
     private var isHiding = false
 
     init {
-        LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this);
+        LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
 
         visibility = View.GONE
         elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
@@ -93,7 +91,7 @@
      *
      * @return true if user education was shown, false otherwise.
      */
-    fun show(stackPosition: PointF) : Boolean{
+    fun show(stackPosition: PointF): Boolean {
         if (visibility == VISIBLE) return false
 
         setAlpha(0f)
@@ -129,6 +127,9 @@
     }
 
     private fun setShouldShow(shouldShow: Boolean) {
-        Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow)
+        context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+                .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply()
     }
-}
\ No newline at end of file
+}
+
+const val PREF_STACK_EDUCATION: String = "HasSeenBubblesOnboarding"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
new file mode 100644
index 0000000..524fa42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java
@@ -0,0 +1,308 @@
+/*
+ * 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.bubbles;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import dalvik.system.CloseGuard;
+
+/**
+ * View that can display a task.
+ */
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
+        MultiWindowTaskListener.Listener {
+
+    public interface Listener {
+        /** Called when the container is ready for launching activities. */
+        default void onInitialized() {}
+
+        /** Called when the container can no longer launch activities. */
+        default void onReleased() {}
+
+        /** Called when a task is created inside the container. */
+        default void onTaskCreated(int taskId, ComponentName name) {}
+
+        /** Called when a task visibility changes. */
+        default void onTaskVisibilityChanged(int taskId, boolean visible) {}
+
+        /** Called when a task is about to be removed from the stack inside the container. */
+        default void onTaskRemovalStarted(int taskId) {}
+
+        /** Called when a task is created inside the container. */
+        default void onBackPressedOnTaskRoot(int taskId) {}
+    }
+
+    private final CloseGuard mGuard = CloseGuard.get();
+
+    private final MultiWindowTaskListener mMultiWindowTaskListener;
+
+    private ActivityManager.RunningTaskInfo mTaskInfo;
+    private WindowContainerToken mTaskToken;
+    private SurfaceControl mTaskLeash;
+    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    private boolean mSurfaceCreated;
+    private boolean mIsInitialized;
+    private Listener mListener;
+
+    private final Rect mTmpRect = new Rect();
+    private final Rect mTmpRootRect = new Rect();
+
+    public TaskView(Context context, MultiWindowTaskListener taskListener) {
+        super(context, null, 0, 0, true /* disableBackgroundLayer */);
+
+        mMultiWindowTaskListener = taskListener;
+        setUseAlpha();
+        getHolder().addCallback(this);
+        mGuard.open("release");
+    }
+
+    /**
+     * Only one listener may be set on the view, throws an exception otherwise.
+     */
+    public void setListener(Listener listener) {
+        if (mListener != null) {
+            throw new IllegalStateException(
+                    "Trying to set a listener when one has already been set");
+        }
+        mListener = listener;
+    }
+
+    /**
+     * Launch an activity represented by {@link ShortcutInfo}.
+     * <p>The owner of this container must be allowed to access the shortcut information,
+     * as defined in {@link LauncherApps#hasShortcutHostPermission()} to use this method.
+     *
+     * @param shortcut the shortcut used to launch the activity.
+     * @param options options for the activity.
+     * @param sourceBounds the rect containing the source bounds of the clicked icon to open
+     *                     this shortcut.
+     */
+    public void startShortcutActivity(@NonNull ShortcutInfo shortcut,
+            @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
+        mMultiWindowTaskListener.setPendingListener(this);
+        prepareActivityOptions(options);
+        LauncherApps service = mContext.getSystemService(LauncherApps.class);
+        try {
+            service.startShortcut(shortcut, sourceBounds, options.toBundle());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Launch a new activity.
+     *
+     * @param pendingIntent Intent used to launch an activity.
+     * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
+     * @param options options for the activity.
+     */
+    public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
+            @NonNull ActivityOptions options) {
+        mMultiWindowTaskListener.setPendingListener(this);
+        prepareActivityOptions(options);
+        try {
+            pendingIntent.send(mContext, 0 /* code */, fillInIntent,
+                    null /* onFinished */, null /* handler */, null /* requiredPermission */,
+                    options.toBundle());
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void prepareActivityOptions(ActivityOptions options) {
+        options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        options.setTaskAlwaysOnTop(true);
+    }
+
+    /**
+     * Call when view position or size has changed. Do not call when animating.
+     */
+    public void onLocationChanged() {
+        if (mTaskToken == null) {
+            return;
+        }
+        // Update based on the screen bounds
+        getBoundsOnScreen(mTmpRect);
+        getRootView().getBoundsOnScreen(mTmpRootRect);
+        if (!mTmpRootRect.contains(mTmpRect)) {
+            mTmpRect.offsetTo(0, 0);
+        }
+
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setBounds(mTaskToken, mTmpRect);
+        // TODO(b/151449487): Enable synchronization
+        mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+    }
+
+    /**
+     * Release this container if it is initialized.
+     */
+    public void release() {
+        performRelease();
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mGuard != null) {
+                mGuard.warnIfOpen();
+                performRelease();
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void performRelease() {
+        getHolder().removeCallback(this);
+        mMultiWindowTaskListener.removeListener(this);
+        resetTaskInfo();
+        mGuard.close();
+        if (mListener != null && mIsInitialized) {
+            mListener.onReleased();
+            mIsInitialized = false;
+        }
+    }
+
+    private void resetTaskInfo() {
+        mTaskInfo = null;
+        mTaskToken = null;
+        mTaskLeash = null;
+    }
+
+    private void updateTaskVisibility() {
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */);
+        mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct);
+        // TODO(b/151449487): Only call callback once we enable synchronization
+        if (mListener != null) {
+            mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
+            SurfaceControl leash) {
+        mTaskInfo = taskInfo;
+        mTaskToken = taskInfo.token;
+        mTaskLeash = leash;
+
+        if (mSurfaceCreated) {
+            // Surface is ready, so just reparent the task to this surface control
+            mTransaction.reparent(mTaskLeash, getSurfaceControl())
+                    .show(mTaskLeash)
+                    .apply();
+        } else {
+            // The surface has already been destroyed before the task has appeared, so go ahead and
+            // hide the task entirely
+            updateTaskVisibility();
+        }
+
+        // TODO: Synchronize show with the resize
+        onLocationChanged();
+        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+
+        if (mListener != null) {
+            mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+        }
+    }
+
+    @Override
+    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+            if (mListener != null) {
+                mListener.onTaskRemovalStarted(taskInfo.taskId);
+            }
+
+            // Unparent the task when this surface is destroyed
+            mTransaction.reparent(mTaskLeash, null).apply();
+            resetTaskInfo();
+        }
+    }
+
+    @Override
+    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        mTaskInfo.taskDescription = taskInfo.taskDescription;
+        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+    }
+
+    @Override
+    public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+        if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) {
+            if (mListener != null) {
+                mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
+            }
+        }
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        mSurfaceCreated = true;
+        if (mListener != null && !mIsInitialized) {
+            mIsInitialized = true;
+            mListener.onInitialized();
+        }
+        if (mTaskToken == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+        // Reparent the task when this surface is created
+        mTransaction.reparent(mTaskLeash, getSurfaceControl())
+                .show(mTaskLeash)
+                .apply();
+        updateTaskVisibility();
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        if (mTaskToken == null) {
+            return;
+        }
+        onLocationChanged();
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        mSurfaceCreated = false;
+        if (mTaskToken == null) {
+            // Nothing to update, task is not yet available
+            return;
+        }
+
+        // Unparent the task when this surface is destroyed
+        mTransaction.reparent(mTaskLeash, null).apply();
+        updateTaskVisibility();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 9f88ee5..fd73207 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -58,6 +58,9 @@
     /** Duration of the expand/collapse target path animation. */
     public static final int EXPAND_COLLAPSE_TARGET_ANIM_DURATION = 175;
 
+    /** Damping ratio for expand/collapse spring. */
+    private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f;
+
     /** Stiffness for the expand/collapse path-following animation. */
     private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000;
 
@@ -510,7 +513,7 @@
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
         return new SpringForce()
-                .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+                .setDampingRatio(DAMPING_RATIO_MEDIUM_LOW_BOUNCY)
                 .setStiffness(SpringForce.STIFFNESS_LOW);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index b7490a5..4c902b9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -72,9 +72,9 @@
     /**
      * Values to use for the default {@link SpringForce} provided to the physics animation layout.
      */
-    public static final int DEFAULT_STIFFNESS = 12000;
+    public static final int SPRING_TO_TOUCH_STIFFNESS = 12000;
     public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
-    private static final int FLING_FOLLOW_STIFFNESS = 500;
+    private static final int CHAIN_STIFFNESS = 600;
     public static final float DEFAULT_BOUNCINESS = 0.9f;
 
     private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
@@ -629,7 +629,7 @@
     public void moveStackFromTouch(float x, float y) {
         // Begin the spring-to-touch catch up animation if needed.
         if (mSpringToTouchOnNextMotionEvent) {
-            springStack(x, y, DEFAULT_STIFFNESS);
+            springStack(x, y, SPRING_TO_TOUCH_STIFFNESS);
             mSpringToTouchOnNextMotionEvent = false;
             mFirstBubbleSpringingToTouch = true;
         } else if (mFirstBubbleSpringingToTouch) {
@@ -762,14 +762,12 @@
     @Override
     SpringForce getSpringForce(DynamicAnimation.ViewProperty property, View view) {
         final ContentResolver contentResolver = mLayout.getContext().getContentResolver();
-        final float stiffness = Settings.Secure.getFloat(contentResolver, "bubble_stiffness",
-                mIsMovingFromFlinging ? FLING_FOLLOW_STIFFNESS : DEFAULT_STIFFNESS /* default */);
         final float dampingRatio = Settings.Secure.getFloat(contentResolver, "bubble_damping",
                 DEFAULT_BOUNCINESS);
 
         return new SpringForce()
                 .setDampingRatio(dampingRatio)
-                .setStiffness(stiffness);
+                .setStiffness(CHAIN_STIFFNESS);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 00ae3a3..6b5f237 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -19,13 +19,15 @@
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.os.Handler;
 import android.view.WindowManager;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleDataRepository;
 import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -39,6 +41,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -68,13 +71,15 @@
             FeatureFlags featureFlags,
             DumpManager dumpManager,
             FloatingContentCoordinator floatingContentCoordinator,
-            BubbleDataRepository bubbleDataRepository,
             SysUiState sysUiState,
             INotificationManager notifManager,
             IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            UiEventLogger uiEventLogger,
+            @Main Handler mainHandler,
+            ShellTaskOrganizer organizer) {
         return BubbleController.create(
                 context,
                 notificationShadeWindowController,
@@ -91,12 +96,14 @@
                 featureFlags,
                 dumpManager,
                 floatingContentCoordinator,
-                bubbleDataRepository,
                 sysUiState,
                 notifManager,
                 statusBarService,
                 windowManager,
                 windowManagerShellWrapper,
-                launcherApps);
+                launcherApps,
+                uiEventLogger,
+                mainHandler,
+                organizer);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
index f447965..ce0786d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubblePersistentRepository.kt
@@ -18,16 +18,11 @@
 import android.content.Context
 import android.util.AtomicFile
 import android.util.Log
-import com.android.systemui.dagger.SysUISingleton
 import java.io.File
 import java.io.FileOutputStream
 import java.io.IOException
-import javax.inject.Inject
 
-@SysUISingleton
-class BubblePersistentRepository @Inject constructor(
-    context: Context
-) {
+class BubblePersistentRepository(context: Context) {
 
     private val bubbleFile: AtomicFile = AtomicFile(File(context.filesDir,
             "overflow_bubbles.xml"), "overflow-bubbles")
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
index c6d5732..e0a7c78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/storage/BubbleVolatileRepository.kt
@@ -19,8 +19,6 @@
 import android.os.UserHandle
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.bubbles.ShortcutKey
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
 
 private const val CAPACITY = 16
 
@@ -28,10 +26,7 @@
  * BubbleVolatileRepository holds the most updated snapshot of list of bubbles for in-memory
  * manipulation.
  */
-@SysUISingleton
-class BubbleVolatileRepository @Inject constructor(
-    private val launcherApps: LauncherApps
-) {
+class BubbleVolatileRepository(private val launcherApps: LauncherApps) {
     /**
      * An ordered set of bubbles based on their natural ordering.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index ba88a59..d13e194 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 
 import com.android.systemui.ForegroundServicesDialog;
-import com.android.systemui.bubbles.BubbleOverflowActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.settings.BrightnessDialog;
@@ -68,12 +67,6 @@
     @ClassKey(ScreenRecordDialog.class)
     public abstract Activity bindScreenRecordDialog(ScreenRecordDialog activity);
 
-    /** Inject into BubbleOverflowActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(BubbleOverflowActivity.class)
-    public abstract Activity bindBubbleOverflowActivity(BubbleOverflowActivity activity);
-
     /** Inject into UsbDebuggingActivity. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index e303754..cb90b61 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -66,6 +66,7 @@
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -263,6 +264,13 @@
         return ActivityManagerWrapper.getInstance();
     }
 
+    /** */
+    @Provides
+    @SysUISingleton
+    public TaskStackChangeListeners provideTaskStackChangeListeners() {
+        return TaskStackChangeListeners.getInstance();
+    }
+
     /** Provides and initializes the {#link BroadcastDispatcher} for SystemUI */
     @Provides
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 6154a4e..f470a6b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -123,6 +123,14 @@
     }
 
     /**
+     * Appends dozing event to the logs
+     * @param suppressed true if dozing is suppressed
+     */
+    public void traceDozingSuppressed(boolean suppressed) {
+        mLogger.logDozingSuppressed(suppressed);
+    }
+
+    /**
      * Appends fling event to the logs
      */
     public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 46cec95..0c9e143 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -64,6 +64,14 @@
         })
     }
 
+    fun logDozingSuppressed(isDozingSuppressed: Boolean) {
+        buffer.log(TAG, INFO, {
+            bool1 = isDozingSuppressed
+        }, {
+            "DozingSuppressed=$bool1"
+        })
+    }
+
     fun logFling(
         expand: Boolean,
         aboveThreshold: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 1e0460b..befb648 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -413,6 +413,7 @@
         pw.print(" state="); pw.println(mState);
         pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
         pw.print(" wakeLock="); pw.println(mWakeLock);
+        pw.print(" isDozeSuppressed="); pw.println(mDozeHost.isDozeSuppressed());
         pw.println("Parts:");
         for (Part p : mParts) {
             p.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index c281ece..7585110 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -30,8 +30,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 public class WorkLockActivityController {
     private static final String TAG = WorkLockActivityController.class.getSimpleName();
@@ -40,16 +40,16 @@
     private final IActivityTaskManager mIatm;
 
     public WorkLockActivityController(Context context) {
-        this(context, ActivityManagerWrapper.getInstance(), ActivityTaskManager.getService());
+        this(context, TaskStackChangeListeners.getInstance(), ActivityTaskManager.getService());
     }
 
     @VisibleForTesting
     WorkLockActivityController(
-            Context context, ActivityManagerWrapper am, IActivityTaskManager iAtm) {
+            Context context, TaskStackChangeListeners tscl, IActivityTaskManager iAtm) {
         mContext = context;
         mIatm = iAtm;
 
-        am.registerTaskStackListener(mLockListener);
+        tscl.registerTaskStackListener(mLockListener);
     }
 
     private void startWorkChallengeInTask(int taskId, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index f6571ef..5b096ea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -42,10 +42,10 @@
 import androidx.constraintlayout.widget.ConstraintSet;
 
 import com.android.settingslib.Utils;
-import com.android.settingslib.media.MediaOutputSliceConstants;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
@@ -93,7 +93,7 @@
     private int mAlbumArtRadius;
     // This will provide the corners for the album art.
     private final ViewOutlineProvider mViewOutlineProvider;
-
+    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
     /**
      * Initialize a new control panel
      * @param context
@@ -104,7 +104,8 @@
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
             ActivityStarter activityStarter, MediaViewController mediaViewController,
             SeekBarViewModel seekBarViewModel, Lazy<MediaDataManager> lazyMediaDataManager,
-            KeyguardDismissUtil keyguardDismissUtil) {
+            KeyguardDismissUtil keyguardDismissUtil, MediaOutputDialogFactory
+            mediaOutputDialogFactory) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
         mActivityStarter = activityStarter;
@@ -112,6 +113,7 @@
         mMediaViewController = mediaViewController;
         mMediaDataManagerLazy = lazyMediaDataManager;
         mKeyguardDismissUtil = keyguardDismissUtil;
+        mMediaOutputDialogFactory = mediaOutputDialogFactory;
         loadDimens();
 
         mViewOutlineProvider = new ViewOutlineProvider() {
@@ -273,13 +275,7 @@
         setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
         setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
         mViewHolder.getSeamless().setOnClickListener(v -> {
-            final Intent intent = new Intent()
-                    .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
-                    .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
-                            data.getPackageName())
-                    .putExtra(MediaOutputSliceConstants.KEY_MEDIA_SESSION_TOKEN, mToken);
-            mActivityStarter.startActivity(intent, false, true /* dismissShade */,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            mMediaOutputDialogFactory.create(data.getPackageName(), true);
         });
 
         ImageView iconView = mViewHolder.getSeamlessIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 11551ac..666a603 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -23,7 +23,6 @@
 import android.widget.ImageView
 import android.widget.SeekBar
 import android.widget.TextView
-import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.R
 import com.android.systemui.util.animation.TransitionLayout
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 3b82999..caef536 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -49,7 +49,7 @@
  * Base dialog for media output UI
  */
 public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
-        MediaOutputController.Callback {
+        MediaOutputController.Callback, Window.Callback {
 
     private static final String TAG = "MediaOutputDialog";
 
@@ -210,4 +210,12 @@
     public void dismissDialog() {
         dismiss();
     }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (!hasFocus && isShowing()) {
+            dismiss();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
index 6cbf065..df9e7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/RotationButtonController.java
@@ -47,6 +47,7 @@
 import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.RotationLockController;
 
@@ -156,7 +157,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
     }
 
     void unregisterListeners() {
@@ -171,7 +172,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
     }
 
     void addRotationCallback(Consumer<Integer> watcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index af851a7..4efe4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -58,7 +58,6 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.shared.system.QuickStepContract;
 
@@ -426,12 +425,6 @@
         if (getDisplay() != null) {
             displayId = getDisplay().getDisplayId();
         }
-        // Bubbles will give us a valid display id if it should get the back event
-        Bubbles Bubbles = Dependency.get(Bubbles.class);
-        int bubbleDisplayId = Bubbles.getExpandedDisplayId(mContext);
-        if (mCode == KeyEvent.KEYCODE_BACK && bubbleDisplayId != INVALID_DISPLAY) {
-            displayId = bubbleDisplayId;
-        }
         if (displayId != INVALID_DISPLAY) {
             ev.setDisplayId(displayId);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index fc9c3d9..18cc746 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.navigationbar.gestural;
 
-import static android.view.Display.INVALID_DISPLAY;
-
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -57,7 +55,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
@@ -71,6 +68,7 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
@@ -387,7 +385,7 @@
             mGestureNavigationSettingsObserver.unregister();
             mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
             mPluginManager.removePluginListener(this);
-            ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+            TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
             DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
 
             try {
@@ -403,7 +401,7 @@
             updateDisplaySize();
             mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
                     mContext.getMainThreadHandler());
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
                     runnable -> (mContext.getMainThreadHandler()).post(runnable),
                     mOnPropertiesChangedListener);
@@ -733,13 +731,7 @@
                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
 
-        // Bubbles will give us a valid display id if it should get the back event
-        final int bubbleDisplayId = Dependency.get(Bubbles.class).getExpandedDisplayId(mContext);
-        if (bubbleDisplayId != INVALID_DISPLAY) {
-            ev.setDisplayId(bubbleDisplayId);
-        } else {
-            ev.setDisplayId(mContext.getDisplay().getDisplayId());
-        }
+        ev.setDisplayId(mContext.getDisplay().getDisplayId());
         InputManager.getInstance().injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 284f41a..fbbda5f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.navigationbar.gestural;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -334,6 +336,7 @@
                 .getDimension(R.dimen.navigation_edge_action_drag_threshold);
         setVisibility(GONE);
 
+        boolean isPrimaryDisplay = mContext.getDisplayId() == DEFAULT_DISPLAY;
         mRegionSamplingHelper = new RegionSamplingHelper(this,
                 new RegionSamplingHelper.SamplingCallback() {
                     @Override
@@ -345,8 +348,14 @@
                     public Rect getSampledRegion(View sampledView) {
                         return mSamplingRect;
                     }
+
+                    @Override
+                    public boolean isSamplingEnabled() {
+                        return isPrimaryDisplay;
+                    }
                 });
         mRegionSamplingHelper.setWindowVisible(true);
+        mShowProtection = !isPrimaryDisplay;
     }
 
     @Override
@@ -366,11 +375,6 @@
         updateIsDark(animate);
     }
 
-    private void setShowProtection(boolean showProtection) {
-        mShowProtection = showProtection;
-        invalidate();
-    }
-
     @Override
     public void setIsLeftPanel(boolean isLeftPanel) {
         mIsLeftPanel = isLeftPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index ac55fa0..7e2433a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -282,7 +283,7 @@
             View view = getChildAt(i);
             if (view == mStatusBarBackground || view == mBackgroundGradient
                     || view == mQSCustomizer) {
-                // Some views are always full width
+                // Some views are always full width or have dependent padding
                 continue;
             }
             LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -291,6 +292,9 @@
             if (view == mQSPanelContainer) {
                 // QS panel lays out some of its content full width
                 mQSPanel.setContentMargins(mContentPaddingStart, mContentPaddingEnd);
+                Pair<Integer, Integer> margins = mQSPanel.getVisualSideMargins();
+                // Apply paddings based on QSPanel
+                mQSCustomizer.setContentPaddings(margins.first, margins.second);
             } else if (view == mHeader) {
                 // The header contains the QQS panel which needs to have special padding, to
                 // visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index fdc0a60e..ca3e4cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.util.AttributeSet;
+import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -1080,6 +1081,10 @@
         updateTileLayoutMargins();
     }
 
+    public Pair<Integer, Integer> getVisualSideMargins() {
+        return new Pair(mVisualMarginStart, mUsingHorizontalLayout ? 0 : mVisualMarginEnd);
+    }
+
     private void updateTileLayoutMargins() {
         int marginEnd = mVisualMarginEnd;
         if (mUsingHorizontalLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index e5ed88c..55b67e0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -132,6 +132,7 @@
         layout.setSpanSizeLookup(mTileAdapter.getSizeLookup());
         mRecyclerView.setLayoutManager(layout);
         mRecyclerView.addItemDecoration(mTileAdapter.getItemDecoration());
+        mRecyclerView.addItemDecoration(mTileAdapter.getMarginItemDecoration());
         DefaultItemAnimator animator = new DefaultItemAnimator();
         animator.setMoveDuration(TileAdapter.MOVE_DURATION);
         mRecyclerView.setItemAnimator(animator);
@@ -221,6 +222,22 @@
         }
     }
 
+    /**
+     * Sets the padding for the RecyclerView. Also, updates the margin between the tiles in the
+     * {@link TileAdapter}.
+     */
+    public void setContentPaddings(int paddingStart, int paddingEnd) {
+        int halfMargin = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) / 2;
+        mTileAdapter.changeHalfMargin(halfMargin);
+        mRecyclerView.setPaddingRelative(
+                paddingStart,
+                mRecyclerView.getPaddingTop(),
+                paddingEnd,
+                mRecyclerView.getPaddingBottom()
+        );
+    }
+
     private void queryTiles() {
         mTileQueryHelper.queryTiles(mHost);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index e049025..b471dfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.view.LayoutInflater;
@@ -75,6 +76,7 @@
     private final List<TileInfo> mTiles = new ArrayList<>();
     private final ItemTouchHelper mItemTouchHelper;
     private final ItemDecoration mDecoration;
+    private final MarginTileDecoration mMarginDecoration;
     private final int mMinNumTiles;
     private int mEditIndex;
     private int mTileDividerIndex;
@@ -97,6 +99,7 @@
         mUiEventLogger = uiEventLogger;
         mItemTouchHelper = new ItemTouchHelper(mCallbacks);
         mDecoration = new TileItemDecoration(context);
+        mMarginDecoration = new MarginTileDecoration();
         mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
         mAccessibilityDelegate = new TileAdapterDelegate();
     }
@@ -123,6 +126,14 @@
         return mDecoration;
     }
 
+    public ItemDecoration getMarginItemDecoration() {
+        return mMarginDecoration;
+    }
+
+    public void changeHalfMargin(int halfMargin) {
+        mMarginDecoration.setHalfMargin(halfMargin);
+    }
+
     public void saveSpecs(QSTileHost host) {
         List<String> newSpecs = new ArrayList<>();
         clearAccessibilityState();
@@ -596,7 +607,6 @@
             mDrawable = context.getDrawable(R.drawable.qs_customize_tile_decoration);
         }
 
-
         @Override
         public void onDraw(Canvas c, RecyclerView parent, State state) {
             super.onDraw(c, parent, state);
@@ -630,6 +640,25 @@
         }
     }
 
+    private static class MarginTileDecoration extends ItemDecoration {
+        private int mHalfMargin;
+
+        public void setHalfMargin(int halfMargin) {
+            mHalfMargin = halfMargin;
+        }
+
+        @Override
+        public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
+                @NonNull RecyclerView parent, @NonNull State state) {
+            if (view instanceof TextView) {
+                super.getItemOffsets(outRect, view, parent, state);
+            } else {
+                outRect.left = mHalfMargin;
+                outRect.right = mHalfMargin;
+            }
+        }
+    }
+
     private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 9c5a3de..ccf2598 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -67,6 +67,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import java.io.PrintWriter;
 import java.util.Collections;
@@ -365,7 +366,7 @@
             mOverviewProxyListenerRegistered = true;
         }
         if (!mTaskListenerRegistered) {
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
+            TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskListener);
             mTaskListenerRegistered = true;
         }
     }
@@ -377,7 +378,7 @@
             mOverviewProxyListenerRegistered = false;
         }
         if (mTaskListenerRegistered) {
-            ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener);
+            TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskListener);
             mTaskListenerRegistered = false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 2b4fa2a..aaa335c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -69,6 +69,7 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
@@ -104,7 +105,6 @@
         public Bitmap image;
         public Consumer<Uri> finisher;
         public GlobalScreenshot.ActionsReadyListener mActionsReadyListener;
-        public int errorMsgResId;
 
         void clearImage() {
             image = null;
@@ -184,6 +184,7 @@
     private final WindowManager.LayoutParams mWindowLayoutParams;
     private final Display mDisplay;
     private final DisplayMetrics mDisplayMetrics;
+    private final AccessibilityManager mAccessibilityManager;
 
     private View mScreenshotLayout;
     private ScreenshotSelectorView mScreenshotSelectorView;
@@ -242,6 +243,7 @@
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mUiEventLogger = uiEventLogger;
+        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
 
         reloadAssets();
         Configuration config = mContext.getResources().getConfiguration();
@@ -319,8 +321,17 @@
             Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
             Consumer<Uri> finisher, Runnable onComplete) {
         // TODO: use task Id, userId, topComponent for smart handler
-
         mOnCompleteRunnable = onComplete;
+
+        if (screenshot == null) {
+            Log.e(TAG, "Got null bitmap from screenshot message");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            finisher.accept(null);
+            mOnCompleteRunnable.run();
+            return;
+        }
+
         if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
             saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
         } else {
@@ -567,12 +578,30 @@
                         .build();
         final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
                 SurfaceControl.captureDisplay(captureArgs);
-        final Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+        Bitmap screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+
+        if (screenshot == null) {
+            Log.e(TAG, "Screenshot bitmap was null");
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            finisher.accept(null);
+            mOnCompleteRunnable.run();
+            return;
+        }
+
         saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
     }
 
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, boolean showFlash) {
+        if (mAccessibilityManager.isEnabled()) {
+            AccessibilityEvent event =
+                    new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            event.setContentDescription(
+                    mContext.getResources().getString(R.string.screenshot_saving_title));
+            mAccessibilityManager.sendAccessibilityEvent(event);
+        }
+
         if (mScreenshotLayout.isAttachedToWindow()) {
             // if we didn't already dismiss for another reason
             if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
@@ -583,14 +612,6 @@
 
         mScreenBitmap = screenshot;
 
-        if (mScreenBitmap == null) {
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            finisher.accept(null);
-            mOnCompleteRunnable.run();
-            return;
-        }
-
         if (!isUserSetupComplete()) {
             // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
             // and sharing shouldn't be exposed to the user.
@@ -632,7 +653,7 @@
                 if (imageData.uri == null) {
                     mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
                     mNotificationsController.notifyScreenshotError(
-                            R.string.screenshot_failed_to_capture_text);
+                            R.string.screenshot_failed_to_save_text);
                 } else {
                     mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
 
@@ -752,7 +773,7 @@
         if (imageData.uri == null) {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
             mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
+                    R.string.screenshot_failed_to_save_text);
         } else {
             mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index df1d789..f0ea597 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -217,13 +217,11 @@
             mParams.mActionsReadyListener.onActionsReady(mImageData);
             mParams.finisher.accept(mImageData.uri);
             mParams.image = null;
-            mParams.errorMsgResId = 0;
         } catch (Exception e) {
             // IOException/UnsupportedOperationException may be thrown if external storage is
             // not mounted
             Slog.e(TAG, "unable to save screenshot", e);
             mParams.clearImage();
-            mParams.errorMsgResId = R.string.screenshot_failed_to_save_text;
             mImageData.reset();
             mParams.mActionsReadyListener.onActionsReady(mImageData);
             mParams.finisher.accept(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
index ac3523b..1b1a51b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
@@ -36,10 +35,9 @@
 import com.android.settingslib.media.InfoMediaManager;
 import com.android.settingslib.media.LocalMediaManager;
 import com.android.settingslib.media.MediaDevice;
-import com.android.settingslib.media.MediaOutputSliceConstants;
 import com.android.settingslib.widget.AdaptiveIcon;
 import com.android.systemui.Dependency;
-import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 
@@ -51,7 +49,7 @@
  */
 public class MediaTransferManager {
     private final Context mContext;
-    private final ActivityStarter mActivityStarter;
+    private final MediaOutputDialogFactory mMediaOutputDialogFactory;
     private MediaDevice mDevice;
     private List<View> mViews = new ArrayList<>();
     private LocalMediaManager mLocalMediaManager;
@@ -74,12 +72,7 @@
             ViewParent parent = view.getParent();
             StatusBarNotification statusBarNotification =
                     getRowForParent(parent).getEntry().getSbn();
-            final Intent intent = new Intent()
-                    .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT)
-                    .putExtra(MediaOutputSliceConstants.EXTRA_PACKAGE_NAME,
-                            statusBarNotification.getPackageName());
-            mActivityStarter.startActivity(intent, false, true /* dismissShade */,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            mMediaOutputDialogFactory.create(statusBarNotification.getPackageName(), true);
             return true;
         }
     };
@@ -107,7 +100,7 @@
 
     public MediaTransferManager(Context context) {
         mContext = context;
-        mActivityStarter = Dependency.get(ActivityStarter.class);
+        mMediaOutputDialogFactory = Dependency.get(MediaOutputDialogFactory.class);
         LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class);
         InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm);
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 433c8b0..ddfa18e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -27,10 +27,10 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
-import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 
@@ -85,38 +85,43 @@
                 for (entry in activeConversationEntries) {
                     if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
                         val important = ranking.channel.isImportantConversation
-                        val layouts = entry.row?.layouts?.asSequence()
+                        var changed = false
+                        entry.row?.layouts?.asSequence()
                                 ?.flatMap(::getLayouts)
                                 ?.mapNotNull { it as? ConversationLayout }
-                                ?: emptySequence()
-                        var changed = false
-                        for (layout in layouts) {
-                            if (important == layout.isImportantConversation) {
-                                continue
-                            }
-                            changed = true
-                            if (important && entry.isMarkedForUserTriggeredMovement) {
-                                // delay this so that it doesn't animate in until after
-                                // the notif has been moved in the shade
-                                mainHandler.postDelayed({
-                                    layout.setIsImportantConversation(
-                                            important, true /* animate */)
-                                }, IMPORTANCE_ANIMATION_DELAY.toLong())
-                            } else {
-                                layout.setIsImportantConversation(important)
-                            }
-                        }
+                                ?.filterNot { it.isImportantConversation == important }
+                                ?.forEach { layout ->
+                                    changed = true
+                                    if (important && entry.isMarkedForUserTriggeredMovement) {
+                                        // delay this so that it doesn't animate in until after
+                                        // the notif has been moved in the shade
+                                        mainHandler.postDelayed(
+                                                {
+                                                    layout.setIsImportantConversation(
+                                                            important,
+                                                            true)
+                                                },
+                                                IMPORTANCE_ANIMATION_DELAY.toLong())
+                                    } else {
+                                        layout.setIsImportantConversation(important, false)
+                                    }
+                                }
                         if (changed) {
                             notificationGroupManager.updateIsolation(entry)
+                            // ensure that the conversation icon isn't hidden
+                            // (ex: if it was showing in the shelf)
+                            entry.row?.updateIconVisibilities()
                         }
                     }
                 }
             }
 
             override fun onEntryInflated(entry: NotificationEntry) {
-                if (!entry.ranking.isConversation) return
+                if (!entry.ranking.isConversation) {
+                    return
+                }
                 fun updateCount(isExpanded: Boolean) {
-                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) {
+                    if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded)) {
                         resetCount(entry.key)
                         entry.row?.let(::resetBadgeUi)
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 113c115..d8d412b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -1478,8 +1478,9 @@
         }
     }
 
-    private void updateIconVisibilities() {
-        // The shelficon is never hidden for children in groups
+    /** Refreshes the visibility of notification icons */
+    public void updateIconVisibilities() {
+        // The shelf icon is never hidden for children in groups
         boolean visible = !isChildInGroup() && mShelfIconVisible;
         for (NotificationContentView l : mLayouts) {
             l.setShelfIconVisible(visible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index fe70c81..17f326b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -147,6 +147,8 @@
                 // hiding the conversationIcon will already do that via its listener.
                 return
             }
+        } else {
+            conversationIconView.isForceHidden = false
         }
         super.setShelfIconVisible(visible)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index efb2469..a5667bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -456,6 +456,7 @@
             return;
         }
         mSuppressed = suppressed;
+        mDozeLog.traceDozingSuppressed(mSuppressed);
         for (Callback callback : mCallbacks) {
             callback.onDozeSuppressedChanged(suppressed);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index a3f14ba..1fdf631a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -23,6 +23,7 @@
 import android.util.MathUtils;
 
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
@@ -122,6 +123,8 @@
      */
     private int mUnlockedStackScrollerPadding;
 
+    private int mLockScreenMode;
+
     /**
      * Refreshes the dimension values.
      */
@@ -171,6 +174,13 @@
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
+    /**
+      * Update lock screen mode for testing different layouts
+      */
+    public void onLockScreenModeChanged(int mode) {
+        mLockScreenMode = mode;
+    }
+
     public float getMinStackScrollerPadding() {
         return mBypassEnabled ? mUnlockedStackScrollerPadding
                 : mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
@@ -185,6 +195,9 @@
     }
 
     private int getExpandedPreferredClockY() {
+        if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+            return mMinTopMargin;
+        }
         return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY()
                 : getExpandedClockPosition();
     }
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 3f636ff..86d4ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -221,6 +221,11 @@
             new KeyguardUpdateMonitorCallback() {
 
                 @Override
+                public void onLockScreenModeChanged(int mode) {
+                    mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+                }
+
+                @Override
                 public void onBiometricAuthenticated(int userId,
                         BiometricSourceType biometricSourceType,
                         boolean isStrongBiometric) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e7c29b6..456e99c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3539,8 +3539,6 @@
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
             if (mNotificationPanelViewController.canPanelBeCollapsed()) {
                 mShadeController.animateCollapsePanels();
-            } else if (mBubblesOptional.isPresent()) {
-                mBubblesOptional.get().performBackPressIfNeeded();
             }
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
index b2c1f48..6a3a69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartRepliesAndActionsInflater.kt
@@ -86,7 +86,7 @@
         sysuiContext: Context,
         notifPackageContext: Context,
         entry: NotificationEntry,
-        existingRepliesAndAction: SmartRepliesAndActions
+        existingRepliesAndAction: SmartRepliesAndActions?
     ): InflatedSmartReplies
 }
 
@@ -103,7 +103,7 @@
         sysuiContext: Context,
         notifPackageContext: Context,
         entry: NotificationEntry,
-        existingRepliesAndAction: SmartRepliesAndActions
+        existingRepliesAndAction: SmartRepliesAndActions?
     ): InflatedSmartReplies {
         val newRepliesAndActions = chooseSmartRepliesAndActions(entry)
         if (!shouldShowSmartReplyView(entry, newRepliesAndActions)) {
@@ -204,24 +204,26 @@
         }
         // Apps didn't provide any smart replies / actions, use those from NAS (if any).
         if (smartReplies == null && smartActions == null) {
-            if (entry.smartReplies.isNotEmpty()
+            val entryReplies = entry.smartReplies
+            val entryActions = entry.smartActions
+            if (entryReplies.isNotEmpty()
                     && freeformRemoteInputActionPair != null
                     && freeformRemoteInputActionPair.second.allowGeneratedReplies
                     && freeformRemoteInputActionPair.second.actionIntent != null) {
                 smartReplies = SmartReplies(
-                        entry.smartReplies,
+                        entryReplies,
                         freeformRemoteInputActionPair.first,
                         freeformRemoteInputActionPair.second.actionIntent,
                         true /* fromAssistant */)
             }
-            if (entry.smartActions.isNotEmpty()
+            if (entryActions.isNotEmpty()
                     && notification.allowSystemGeneratedContextualActions) {
                 val systemGeneratedActions: List<Notification.Action> = when {
                     activityManagerWrapper.isLockTaskKioskModeActive ->
                         // Filter actions if we're in kiosk-mode - we don't care about screen
                         // pinning mode, since notifications aren't shown there anyway.
-                        filterAllowlistedLockTaskApps(entry.smartActions)
-                    else -> entry.smartActions
+                        filterAllowlistedLockTaskApps(entryActions)
+                    else -> entryActions
                 }
                 smartActions = SmartActions(systemGeneratedActions, true /* fromAssistant */)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 2081cfe..735338f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -90,6 +90,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.VolumeDialog;
 import com.android.systemui.plugins.VolumeDialogController;
@@ -519,6 +520,7 @@
                 Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
                 Intent intent = new Intent(Settings.Panel.ACTION_VOLUME);
                 dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+                Dependency.get(MediaOutputDialogFactory.class).dismiss();
                 Dependency.get(ActivityStarter.class).startActivity(intent,
                         true /* dismissShade */);
             });
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 247baf8..f55445c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -26,6 +26,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
@@ -86,12 +87,19 @@
 
     @SysUISingleton
     @Provides
+    static PipBoundsState providePipBoundsState() {
+        return new PipBoundsState();
+    }
+
+    @SysUISingleton
+    @Provides
     static PipTaskOrganizer providePipTaskOrganizer(Context context,
+            PipBoundsState pipBoundsState,
             PipBoundsHandler pipBoundsHandler,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
-        return new PipTaskOrganizer(context, pipBoundsHandler,
+        return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 pipSurfaceTransactionHelper, splitScreenOptional, displayController,
                 pipUiEventLogger, shellTaskOrganizer);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a948103..9281a09 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -55,9 +55,9 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -104,7 +104,7 @@
     private final DisplayImeController mDisplayImeController;
     private final InputConsumerController mInputConsumerController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final ActivityManagerWrapper mActivityManagerWrapper;
+    private final TaskStackChangeListeners mTaskStackChangeListeners;
     private final NavigationModeController mNavigationModeController;
     private final ScreenLifecycle mScreenLifecycle;
     private final SysUiState mSysUiState;
@@ -125,7 +125,7 @@
             ConfigurationController configurationController,
             InputConsumerController inputConsumerController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            ActivityManagerWrapper activityManagerWrapper,
+            TaskStackChangeListeners taskStackChangeListeners,
             DisplayImeController displayImeController,
             NavigationModeController navigationModeController,
             ScreenLifecycle screenLifecycle,
@@ -140,7 +140,7 @@
         mConfigurationController = configurationController;
         mInputConsumerController = inputConsumerController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
-        mActivityManagerWrapper = activityManagerWrapper;
+        mTaskStackChangeListeners = taskStackChangeListeners;
         mDisplayImeController = displayImeController;
         mNavigationModeController = navigationModeController;
         mScreenLifecycle = screenLifecycle;
@@ -205,7 +205,7 @@
         });
 
         // Handle for system task stack changes.
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onTaskStackChanged() {
@@ -276,7 +276,7 @@
         };
         mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback);
 
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
@@ -388,7 +388,7 @@
             }
         });
 
-        mActivityManagerWrapper.registerTaskStackListener(
+        mTaskStackChangeListeners.registerTaskStackListener(
                 new TaskStackChangeListener() {
                     @Override
                     public void onTaskCreated(int taskId, ComponentName componentName) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index ae7b108..975757a 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -32,6 +32,7 @@
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
@@ -71,14 +72,16 @@
             DisplayController displayController,
             PipAppOpsListener pipAppOpsListener,
             PipBoundsHandler pipBoundsHandler,
+            PipBoundsState pipBoundsState,
             PipMediaController pipMediaController,
             PipMenuActivityController pipMenuActivityController,
             PipTaskOrganizer pipTaskOrganizer,
             PipTouchHandler pipTouchHandler,
             WindowManagerShellWrapper windowManagerShellWrapper) {
         return new PipController(context, displayController,
-                pipAppOpsListener, pipBoundsHandler, pipMediaController, pipMenuActivityController,
-                pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
+                pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController,
+                pipMenuActivityController, pipTaskOrganizer, pipTouchHandler,
+                windowManagerShellWrapper);
     }
 
     @SysUISingleton
@@ -94,6 +97,12 @@
 
     @SysUISingleton
     @Provides
+    static PipBoundsState providePipBoundsState() {
+        return new PipBoundsState();
+    }
+
+    @SysUISingleton
+    @Provides
     static PipBoundsHandler providesPipBoundsHandler(Context context) {
         return new PipBoundsHandler(context);
     }
@@ -109,21 +118,23 @@
     @Provides
     static PipTouchHandler providesPipTouchHandler(Context context,
             PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
+            PipBoundsState pipBoundsState,
             PipTaskOrganizer pipTaskOrganizer,
             FloatingContentCoordinator floatingContentCoordinator,
             PipUiEventLogger pipUiEventLogger) {
         return new PipTouchHandler(context, menuActivityController, pipBoundsHandler,
-                pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+                pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
     }
 
     @SysUISingleton
     @Provides
     static PipTaskOrganizer providesPipTaskOrganizer(Context context,
+            PipBoundsState pipBoundsState,
             PipBoundsHandler pipBoundsHandler,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
             PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
-        return new PipTaskOrganizer(context, pipBoundsHandler,
+        return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 pipSurfaceTransactionHelper, splitScreenOptional, displayController,
                 pipUiEventLogger, shellTaskOrganizer);
     }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index db87845..2b4ed4e 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -82,6 +82,10 @@
 
         <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
                   android:exported="false" />
+
+        <activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
+                  android:exported="false" />
+
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 353fe62..35fe1ba 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -168,7 +168,7 @@
         verify(mMockListener2).onClockChanged(captor2.capture());
         assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
         assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS);
-        assertThat(captor1.getValue()).isNotSameAs(captor2.getValue());
+        assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
index 1638ea1..71a0434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
@@ -29,8 +29,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -50,7 +50,7 @@
 
     private SizeCompatModeActivityController mController;
     private TaskStackChangeListener mTaskStackListener;
-    private @Mock ActivityManagerWrapper mMockAm;
+    private @Mock TaskStackChangeListeners mMockTaskListeners;
     private @Mock RestartActivityButton mMockButton;
     private @Mock IBinder mMockActivityToken;
 
@@ -59,7 +59,7 @@
         MockitoAnnotations.initMocks(this);
         doReturn(true).when(mMockButton).show();
 
-        mController = new SizeCompatModeActivityController(mContext, mMockAm,
+        mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
                 new CommandQueue(mContext)) {
             @Override
             RestartActivityButton createRestartButton(Context context) {
@@ -69,7 +69,7 @@
 
         ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
                 ArgumentCaptor.forClass(TaskStackChangeListener.class);
-        verify(mMockAm).registerTaskStackListener(listenerCaptor.capture());
+        verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
         mTaskStackListener = listenerCaptor.getValue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 0a51b26..3c248c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -24,6 +24,7 @@
 import static android.view.MotionEvent.ACTION_UP;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK;
 
+import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS;
 import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
 
 import static junit.framework.Assert.assertEquals;
@@ -35,7 +36,6 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -69,6 +69,9 @@
 @RunWith(AndroidTestingRunner.class)
 public class MagnificationModeSwitchTest extends SysuiTestCase {
 
+    private static final float FADE_IN_ALPHA = 1f;
+    private static final float FADE_OUT_ALPHA = 0f;
+
     private ImageView mSpyImageView;
     @Mock
     private WindowManager mWindowManager;
@@ -87,49 +90,47 @@
         ).when(mWindowManager).getMaximumWindowMetrics();
         mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
         mSpyImageView = Mockito.spy(new ImageView(mContext));
-        doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
-                mTouchListenerCaptor.capture());
-        initMockImageViewAndAnimator();
+        resetMockImageViewAndAnimator();
 
         mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView);
     }
 
     @Test
-    public void removeButton_removeView() {
+    public void removeButton_buttonIsShowing_removeView() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
 
         mMagnificationModeSwitch.removeButton();
 
         verify(mWindowManager).removeView(mSpyImageView);
-        // First invocation is in showButton.
-        verify(mViewPropertyAnimator, times(2)).cancel();
+        verify(mViewPropertyAnimator).cancel();
     }
 
     @Test
     public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-
-        verify(mSpyImageView).setAlpha(1.0f);
         verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_IN_ALPHA);
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+
         ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
         verify(mViewPropertyAnimator).withEndAction(captor.capture());
         verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class));
 
         captor.getValue().run();
 
-        // First invocation is in showButton.
-        verify(mViewPropertyAnimator, times(2)).cancel();
+        verify(mViewPropertyAnimator).cancel();
         verify(mWindowManager).removeView(mSpyImageView);
     }
 
     @Test
-    public void onConfigurationChanged_setImageResource() {
+    public void onConfigurationChanged_buttonIsShowing_setImageResource() {
         mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        resetMockImageViewAndAnimator();
+
         mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_DENSITY);
 
-        verify(mSpyImageView, times(2)).setImageResource(
+        verify(mSpyImageView).setImageResource(
                 getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN));
     }
 
@@ -162,7 +163,6 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, 0, ACTION_DOWN, 100, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         verify(mViewPropertyAnimator).cancel();
 
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
@@ -173,9 +173,8 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout() + 10, ACTION_UP, 100 + offset, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     @Test
@@ -193,9 +192,8 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     @Test
@@ -216,9 +214,8 @@
         resetMockImageViewAndAnimator();
         listener.onTouch(mSpyImageView, MotionEvent.obtain(
                 0, ViewConfiguration.getTapTimeout(), ACTION_CANCEL, 100 + offset, 100, 0));
-        verify(mSpyImageView).setAlpha(1.0f);
         assertModeUnchanged(previousMode);
-        assertShowButtonAnimation();
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
     @Test
@@ -249,37 +246,55 @@
         verifyTapAction(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
     }
 
+    @Test
+    public void showButton_showFadeOutAnimation_fadeOutAnimationCanceled() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+        resetMockImageViewAndAnimator();
+
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+        verify(mViewPropertyAnimator).cancel();
+        assertEquals(1f, mSpyImageView.getAlpha());
+        assertShowFadingAnimation(FADE_OUT_ALPHA);
+    }
+
     private void assertModeUnchanged(int expectedMode) {
         final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
         assertEquals(expectedMode, actualMode);
     }
 
-    private void assertShowButtonAnimation() {
-        verify(mViewPropertyAnimator).cancel();
-        verify(mViewPropertyAnimator).setDuration(anyLong());
-        verify(mViewPropertyAnimator).alpha(anyFloat());
+    private void assertShowFadingAnimation(float alpha) {
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        if (alpha == FADE_IN_ALPHA) { // Fade-in
+            verify(mSpyImageView).postOnAnimation(runnableCaptor.capture());
+        } else { // Fade-out
+            verify(mSpyImageView).postOnAnimationDelayed(runnableCaptor.capture(), anyLong());
+        }
+        resetMockAnimator();
+
+        runnableCaptor.getValue().run();
+
+        verify(mViewPropertyAnimator).setDuration(eq(FADING_ANIMATION_DURATION_MS));
+        verify(mViewPropertyAnimator).alpha(alpha);
         verify(mViewPropertyAnimator).start();
     }
 
-    private void initMockImageViewAndAnimator() {
+    private void resetMockImageViewAndAnimator() {
+        Mockito.reset(mSpyImageView);
+        doAnswer(invocation -> null).when(mSpyImageView).setOnTouchListener(
+                mTouchListenerCaptor.capture());
+        resetMockAnimator();
+    }
+
+    private void resetMockAnimator() {
+        Mockito.reset(mViewPropertyAnimator);
         when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
         when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
                 mViewPropertyAnimator);
-
         when(mSpyImageView.animate()).thenReturn(mViewPropertyAnimator);
-        doAnswer(invocation -> {
-            Runnable run = invocation.getArgument(0);
-            run.run();
-            return null;
-        }).when(mSpyImageView).postDelayed(any(), anyLong());
-    }
-
-    private void resetMockImageViewAndAnimator() {
-        Mockito.reset(mViewPropertyAnimator);
-        Mockito.reset(mSpyImageView);
-        initMockImageViewAndAnimator();
     }
 
     /**
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 99e39b8..e24f4ca3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -222,7 +222,7 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
         event.recycle();
         // THEN the event is passed to the FingerprintManager
-        verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+        verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
                 eq(0f), eq(0f));
         // AND the scrim and dot is shown
         verify(mUdfpsView).showScrimAndDot();
@@ -236,7 +236,7 @@
         // WHEN fingerprint is requested because of AOD interrupt
         mUdfpsController.onAodInterrupt(0, 0);
         // THEN the event is passed to the FingerprintManager
-        verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+        verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
                 anyFloat(), anyFloat());
         // AND the scrim and dot is shown
         verify(mUdfpsView).showScrimAndDot();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 08ccd854..b082d17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -91,6 +91,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -194,6 +195,8 @@
 
     @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
 
+    @Mock private BubbleLogger mBubbleLogger;
+
     private BubbleData mBubbleData;
 
     private TestableLooper mTestableLooper;
@@ -249,7 +252,7 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext);
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
         when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -273,7 +276,10 @@
                 mStatusBarService,
                 mWindowManager,
                 mWindowManagerShellWrapper,
-                mLauncherApps);
+                mLauncherApps,
+                mBubbleLogger,
+                mock(Handler.class),
+                mock(ShellTaskOrganizer.class));
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
         // Get a reference to the BubbleController's entry listener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 6e2c7e5..0c872db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.bubbles;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
@@ -94,6 +95,8 @@
     private PendingIntent mExpandIntent;
     @Mock
     private PendingIntent mDeleteIntent;
+    @Mock
+    private BubbleLogger mBubbleLogger;
 
     @Captor
     private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
@@ -133,7 +136,7 @@
         mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
         mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
 
-        mBubbleData = new BubbleData(getContext());
+        mBubbleData = new BubbleData(getContext(), mBubbleLogger);
 
         // Used by BubbleData to set lastAccessedTime
         when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
@@ -801,47 +804,48 @@
 
     private void assertBubbleAdded(Bubble expected) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.addedBubble).named("addedBubble").isEqualTo(expected);
+        assertWithMessage("addedBubble").that(update.addedBubble).isEqualTo(expected);
     }
 
     private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.removedBubbles).named("removedBubbles")
+        assertWithMessage("removedBubbles").that(update.removedBubbles)
                 .isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
     }
 
     private void assertOrderNotChanged() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.orderChanged).named("orderChanged").isFalse();
+        assertWithMessage("orderChanged").that(update.orderChanged).isFalse();
     }
 
     private void assertOrderChangedTo(Bubble... order) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.orderChanged).named("orderChanged").isTrue();
-        assertThat(update.bubbles).named("bubble order").isEqualTo(ImmutableList.copyOf(order));
+        assertWithMessage("orderChanged").that(update.orderChanged).isTrue();
+        assertWithMessage("bubble order").that(update.bubbles)
+                .isEqualTo(ImmutableList.copyOf(order));
     }
 
     private void assertSelectionNotChanged() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isFalse();
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isFalse();
     }
 
     private void assertSelectionChangedTo(Bubble bubble) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isTrue();
-        assertThat(update.selectedBubble).named("selectedBubble").isEqualTo(bubble);
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+        assertWithMessage("selectedBubble").that(update.selectedBubble).isEqualTo(bubble);
     }
 
     private void assertSelectionCleared() {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.selectionChanged).named("selectionChanged").isTrue();
-        assertThat(update.selectedBubble).named("selectedBubble").isNull();
+        assertWithMessage("selectionChanged").that(update.selectionChanged).isTrue();
+        assertWithMessage("selectedBubble").that(update.selectedBubble).isNull();
     }
 
     private void assertExpandedChangedTo(boolean expected) {
         BubbleData.Update update = mUpdateCaptor.getValue();
-        assertThat(update.expandedChanged).named("expandedChanged").isTrue();
-        assertThat(update.expanded).named("expanded").isEqualTo(expected);
+        assertWithMessage("expandedChanged").that(update.expandedChanged).isTrue();
+        assertWithMessage("expanded").that(update.expanded).isEqualTo(expected);
     }
 
     private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
@@ -913,4 +917,4 @@
         setCurrentTime(time);
         mBubbleData.setExpanded(shouldBeExpanded);
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
new file mode 100644
index 0000000..7c1b414
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.bubbles;
+
+import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class MultiWindowTaskListenerTest extends SysuiTestCase {
+
+    @Mock
+    ShellTaskOrganizer mOrganizer;
+    @Mock
+    MultiWindowTaskListener.Listener mPendingListener;
+    @Mock
+    SurfaceControl mLeash;
+    @Mock
+    ActivityManager.RunningTaskInfo mTaskInfo;
+    @Mock
+    WindowContainerToken mToken;
+
+    Handler mHandler;
+    MultiWindowTaskListener mTaskListener;
+    TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = new Handler(mTestableLooper.getLooper());
+
+        mTaskInfo = new ActivityManager.RunningTaskInfo();
+        mTaskInfo.token = mToken;
+
+        mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
+    }
+
+    private void addTaskAndVerify() {
+        mTaskListener.setPendingListener(mPendingListener);
+        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+        mTestableLooper.processAllMessages();
+        verify(mPendingListener).onTaskAppeared(eq(mTaskInfo), eq(mLeash));
+    }
+
+    @Test
+    public void testListenForMultiWindowMode() {
+        mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer);
+        verify(mOrganizer).addListener(eq(mTaskListener), eq(TASK_LISTENER_TYPE_MULTI_WINDOW));
+    }
+
+    @Test
+    public void testRemovePendingListener() {
+        addTaskAndVerify();
+        reset(mPendingListener);
+
+        mTaskListener.removeListener(mPendingListener);
+
+        // If it was removed, our pendingListener shouldn't get triggered:
+        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskListener.onTaskInfoChanged(mTaskInfo);
+        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+        mTaskListener.onTaskVanished(mTaskInfo);
+
+        mTestableLooper.processAllMessages();
+        verify(mPendingListener, never()).onTaskAppeared(any(), any());
+        verify(mPendingListener, never()).onTaskInfoChanged(any());
+        verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
+        verify(mPendingListener, never()).onTaskVanished(any());
+    }
+
+    @Test
+    public void testOnTaskAppeared() {
+        addTaskAndVerify();
+        verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mToken), eq(true));
+    }
+
+    @Test
+    public void testOnTaskAppeared_nullListener() {
+        mTaskListener.onTaskAppeared(mTaskInfo, mLeash);
+        mTestableLooper.processAllMessages();
+
+        verify(mOrganizer, never()).setInterceptBackPressedOnTaskRoot(any(), anyBoolean());
+        verify(mPendingListener, never()).onTaskAppeared(any(), any());
+    }
+
+    @Test
+    public void testOnTaskVanished() {
+        addTaskAndVerify();
+        mTaskListener.onTaskVanished(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener).onTaskVanished(eq(mTaskInfo));
+    }
+
+    @Test
+    public void testOnTaskVanished_neverAdded() {
+        mTaskListener.onTaskVanished(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener, never()).onTaskVanished(any());
+    }
+
+    @Test
+    public void testOnTaskInfoChanged() {
+        addTaskAndVerify();
+        mTaskListener.onTaskInfoChanged(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener).onTaskInfoChanged(eq(mTaskInfo));
+    }
+
+    @Test
+    public void testOnTaskInfoChanged_neverAdded() {
+        mTaskListener.onTaskInfoChanged(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener, never()).onTaskInfoChanged(any());
+    }
+
+    @Test
+    public void testOnBackPressedOnTaskRoot() {
+        addTaskAndVerify();
+        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener).onBackPressedOnTaskRoot(eq(mTaskInfo));
+    }
+
+    @Test
+    public void testOnBackPressedOnTaskRoot_neverAdded() {
+        mTaskListener.onBackPressedOnTaskRoot(mTaskInfo);
+        mTestableLooper.processAllMessages();
+
+        verify(mPendingListener, never()).onBackPressedOnTaskRoot(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 1eaa6a4..cbacd53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -91,6 +91,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
@@ -188,6 +189,8 @@
     private LauncherApps mLauncherApps;
     @Mock
     private WindowManagerShellWrapper mWindowManagerShellWrapper;
+    @Mock
+    private BubbleLogger mBubbleLogger;
 
     private BubbleData mBubbleData;
 
@@ -251,7 +254,7 @@
                         mock(HeadsUpManager.class),
                         mock(Handler.class)
                 );
-        mBubbleData = new BubbleData(mContext);
+        mBubbleData = new BubbleData(mContext, mBubbleLogger);
         when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
         mBubbleController = new TestableBubbleController(
                 mContext,
@@ -275,7 +278,10 @@
                 mStatusBarService,
                 mWindowManager,
                 mWindowManagerShellWrapper,
-                mLauncherApps);
+                mLauncherApps,
+                mBubbleLogger,
+                mock(Handler.class),
+                mock(ShellTaskOrganizer.class));
         mBubbleController.addNotifCallback(mNotifCallback);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
new file mode 100644
index 0000000..6f3968d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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.bubbles;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceControl;
+import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.wm.shell.ShellTaskOrganizer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration.
+public class TaskViewTest extends SysuiTestCase {
+
+    @Mock
+    TaskView.Listener mViewListener;
+    @Mock
+    ActivityManager.RunningTaskInfo mTaskInfo;
+    @Mock
+    WindowContainerToken mToken;
+    @Mock
+    ShellTaskOrganizer mOrganizer;
+    @Mock
+    MultiWindowTaskListener mTaskListener;
+
+    SurfaceSession mSession;
+    SurfaceControl mLeash;
+
+    Context mContext;
+    TaskView mTaskView;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mLeash = new SurfaceControl.Builder(mSession)
+                .setName("test")
+                .build();
+
+        mContext = getContext();
+
+        when(mTaskListener.getTaskOrganizer()).thenReturn(mOrganizer);
+        mTaskInfo = new ActivityManager.RunningTaskInfo();
+        mTaskInfo.token = mToken;
+        mTaskInfo.taskId = 314;
+        mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class);
+
+        mTaskView = new TaskView(mContext, mTaskListener);
+        mTaskView.setListener(mViewListener);
+    }
+
+    @After
+    public void tearDown() {
+        if (mTaskView != null) {
+            mTaskView.release();
+        }
+    }
+
+    @Test
+    public void testSetPendingListener_throwsException() {
+        TaskView taskView = new TaskView(mContext, mTaskListener);
+        taskView.setListener(mViewListener);
+        try {
+            taskView.setListener(mViewListener);
+        } catch (IllegalStateException e) {
+            // pass
+            return;
+        }
+        fail("Expected IllegalStateException");
+    }
+
+    @Test
+    public void testStartActivity() {
+        ActivityOptions options = ActivityOptions.makeBasic();
+        mTaskView.startActivity(mock(PendingIntent.class), null, options);
+
+        verify(mTaskListener).setPendingListener(eq(mTaskView));
+        assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW);
+        assertThat(options.getTaskAlwaysOnTop()).isTrue();
+    }
+
+    @Test
+    public void testOnTaskAppeared_noSurface() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onInitialized();
+        // If there's no surface the task should be made invisible
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testOnTaskAppeared_withSurface() {
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+
+        verify(mViewListener).onTaskCreated(eq(mTaskInfo.taskId), any());
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceCreated_noTask() {
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        verify(mViewListener).onInitialized();
+        // No task, no visibility change
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceCreated_withTask() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+
+        verify(mViewListener).onInitialized();
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(true));
+    }
+
+    @Test
+    public void testSurfaceDestroyed_noTask() {
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskView.surfaceCreated(sh);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener, never()).onTaskVisibilityChanged(anyInt(), anyBoolean());
+    }
+
+    @Test
+    public void testSurfaceDestroyed_withTask() {
+        SurfaceHolder sh = mock(SurfaceHolder.class);
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(sh);
+        reset(mViewListener);
+        mTaskView.surfaceDestroyed(sh);
+
+        verify(mViewListener).onTaskVisibilityChanged(eq(mTaskInfo.taskId), eq(false));
+    }
+
+    @Test
+    public void testOnReleased() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.release();
+
+        verify(mTaskListener).removeListener(eq(mTaskView));
+        verify(mViewListener).onReleased();
+    }
+
+    @Test
+    public void testOnTaskVanished() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.surfaceCreated(mock(SurfaceHolder.class));
+        mTaskView.onTaskVanished(mTaskInfo);
+
+        verify(mViewListener).onTaskRemovalStarted(eq(mTaskInfo.taskId));
+    }
+
+    @Test
+    public void testOnBackPressedOnTaskRoot() {
+        mTaskView.onTaskAppeared(mTaskInfo, mLeash);
+        mTaskView.onBackPressedOnTaskRoot(mTaskInfo);
+
+        verify(mViewListener).onBackPressedOnTaskRoot(eq(mTaskInfo.taskId));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 87ea22a..27c6fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.LauncherApps;
+import android.os.Handler;
 import android.view.WindowManager;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -35,10 +36,10 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 
-
 /**
  * Testable BubbleController subclass that immediately synchronizes surfaces.
  */
@@ -66,14 +67,18 @@
             IStatusBarService statusBarService,
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
-            LauncherApps launcherApps) {
+            LauncherApps launcherApps,
+            BubbleLogger bubbleLogger,
+            Handler mainHandler,
+            ShellTaskOrganizer shellTaskOrganizer) {
         super(context,
                 notificationShadeWindowController, statusBarStateController, shadeController,
                 data, Runnable::run, configurationController, interruptionStateProvider,
                 zenModeController, lockscreenUserManager, groupManager, entryManager,
                 notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
                 dataRepository, sysUiState, notificationManager, statusBarService,
-                windowManager, windowManagerShellWrapper, launcherApps);
+                windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger,
+                mainHandler, shellTaskOrganizer);
         setInflateSynchronously(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index 3439fe5..cd5740d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -41,8 +41,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +62,7 @@
     private static final int TASK_ID = 444;
 
     private @Mock Context mContext;
-    private @Mock ActivityManagerWrapper mActivityManager;
+    private @Mock TaskStackChangeListeners mTaskStackChangeListeners;
     private @Mock IActivityTaskManager mIActivityTaskManager;
 
     private WorkLockActivityController mController;
@@ -78,10 +78,10 @@
         // Construct controller. Save the TaskStackListener for injecting events.
         final ArgumentCaptor<TaskStackChangeListener> listenerCaptor =
                 ArgumentCaptor.forClass(TaskStackChangeListener.class);
-        mController = new WorkLockActivityController(mContext, mActivityManager,
+        mController = new WorkLockActivityController(mContext, mTaskStackChangeListeners,
                 mIActivityTaskManager);
 
-        verify(mActivityManager).registerTaskStackListener(listenerCaptor.capture());
+        verify(mTaskStackChangeListeners).registerTaskStackListener(listenerCaptor.capture());
         mTaskStackListener = listenerCaptor.getValue();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 81139f19..182a056 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -38,6 +38,7 @@
 import androidx.lifecycle.LiveData
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.media.dialog.MediaOutputDialogFactory
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.util.animation.TransitionLayout
@@ -92,6 +93,7 @@
     @Mock private lateinit var mediaDataManager: MediaDataManager
     @Mock private lateinit var expandedSet: ConstraintSet
     @Mock private lateinit var collapsedSet: ConstraintSet
+    @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
     private lateinit var appIcon: ImageView
     private lateinit var appName: TextView
     private lateinit var albumView: ImageView
@@ -126,7 +128,8 @@
         whenever(mediaViewController.collapsedLayout).thenReturn(collapsedSet)
 
         player = MediaControlPanel(context, bgExecutor, activityStarter, mediaViewController,
-                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil)
+                seekBarViewModel, Lazy { mediaDataManager }, keyguardDismissUtil,
+                mediaOutputDialogFactory)
         whenever(seekBarViewModel.progress).thenReturn(seekBarData)
 
         // Mock out a view holder for the player to attach to.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index 9b6dd05..3494bd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -32,15 +32,11 @@
 import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_LONGPRESS;
 import static com.android.systemui.navigationbar.buttons.KeyButtonView.NavBarButtonEvent.NAVBAR_OVERVIEW_BUTTON_TAP;
 
-import static junit.framework.Assert.assertEquals;
-
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.hardware.input.InputManager;
 import android.testing.AndroidTestingRunner;
@@ -151,19 +147,4 @@
             verify(mUiEventLogger, times(1)).log(expected);
         }
     }
-
-    @Test
-    public void testBubbleEvents_bubbleExpanded() {
-        when(mBubbles.getExpandedDisplayId(mContext)).thenReturn(3);
-
-        int action = KeyEvent.ACTION_DOWN;
-        int flags = 0;
-        int code = KeyEvent.KEYCODE_BACK;
-        mKeyButtonView.setCode(code);
-        mKeyButtonView.sendEvent(action, flags);
-
-        verify(mInputManager, times(1)).injectInputEvent(mInputEventCaptor.capture(),
-                anyInt());
-        assertEquals(3, mInputEventCaptor.getValue().getDisplayId());
-    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
new file mode 100644
index 0000000..fde56ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RecyclerViewActivity.java
@@ -0,0 +1,80 @@
+/*
+ * 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.screenshot;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.LayoutParams;
+
+import java.util.Random;
+
+public class RecyclerViewActivity extends Activity {
+    public static final int CHILD_VIEW_HEIGHT = 300;
+    private static final int CHILD_VIEWS = 12;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        RecyclerView recyclerView = new RecyclerView(this);
+        recyclerView.setLayoutManager(new LinearLayoutManager(this));
+        recyclerView.setAdapter(new TestAdapter());
+        recyclerView.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+        setContentView(recyclerView);
+    }
+
+    static final class TestViewHolder extends RecyclerView.ViewHolder {
+        TestViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+    static final class TestAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+        private final Random mRandom = new Random();
+
+        @Override
+        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            return new TestViewHolder(new TextView(parent.getContext()));
+        }
+
+        @Override
+        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+            TextView view = (TextView) holder.itemView;
+            view.setText("Child #" + position);
+            view.setTextColor(Color.WHITE);
+            view.setTextSize(30f);
+            view.setBackgroundColor(
+                    Color.rgb(mRandom.nextFloat(), mRandom.nextFloat(), mRandom.nextFloat()));
+            view.setMinHeight(CHILD_VIEW_HEIGHT);
+        }
+
+        @Override
+        public int getItemCount() {
+            return CHILD_VIEWS;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
index 72e6df2..724ea02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
@@ -80,7 +80,7 @@
         val background2 = processor.processArtwork(context, artwork)!!
         // THEN the two bitmaps are the same
         // Note: This is currently broken and trying to use caching causes issues
-        assertThat(background1).isNotSameAs(background2)
+        assertThat(background1).isNotSameInstanceAs(background2)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index d0dfb171..524ead2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -34,6 +34,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -64,6 +65,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
         allowTestableLooperAsMainThread();
         when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
         mDynamicChildBindController =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 3e1616c..d04d8ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
@@ -346,7 +348,7 @@
         setSmartActions(mEntry.getKey(), null);
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        assertNull(mEntry.getSmartActions());
+        assertThat(mEntry.getSmartActions()).isEmpty();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
index 79fa436..5898664 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt
@@ -118,7 +118,7 @@
         val people = viewModel.people.toList()
         assertThat(people.size).isEqualTo(1)
         assertThat(people[0].name).isEqualTo("name")
-        assertThat(people[0].icon).isSameAs(fakePerson.avatar)
+        assertThat(people[0].icon).isSameInstanceAs(fakePerson.avatar)
 
         people[0].onClick()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index c2091da..d08b2b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -25,8 +25,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AppOpsManager;
-import android.util.ArraySet;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -37,6 +35,7 @@
 
 import com.android.internal.widget.NotificationExpandButton;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,6 +50,8 @@
     @Before
     @UiThreadTest
     public void setup() {
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
+
         mView = new NotificationContentView(mContext, null);
         ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null);
         ExpandableNotificationRow mockRow = spy(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 1255b6d..8a5afe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -152,6 +153,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(SmartReplyController.class);
+        mDependency.injectMockDependency(MediaOutputDialogFactory.class);
 
         mHandler = Handler.createAsync(TestableLooper.get(this).getLooper());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index fb37ed5..847e0a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,6 +48,7 @@
 import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.bubbles.BubblesTestActivity;
 import com.android.systemui.media.MediaFeatureFlag;
+import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -115,6 +116,7 @@
         mTestLooper = testLooper;
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
+        dependency.injectMockDependency(MediaOutputDialogFactory.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
         mGroupMembershipManager = new NotificationGroupManagerLegacy(
                 mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 7ee27c9..3868443 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -350,7 +350,7 @@
         mAccessibiltyDelegate.onInitializeAccessibilityNodeInfo(mView, nodeInfo);
 
         List<AccessibilityNodeInfo.AccessibilityAction> actionList = nodeInfo.getActionList();
-        assertThat(actionList).containsAllIn(
+        assertThat(actionList).containsAtLeastElementsIn(
                 new AccessibilityNodeInfo.AccessibilityAction[] {
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD,
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index e93c5db..8ee15bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -183,8 +183,7 @@
         SmartRepliesAndActions repliesAndActions =
                 mSmartRepliesInflater.chooseSmartRepliesAndActions(mEntry);
 
-        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(
-                mEntry.getSmartReplies());
+        assertThat(repliesAndActions.smartReplies.choices).isEqualTo(mEntry.getSmartReplies());
         assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue();
         assertThat(repliesAndActions.smartActions).isNull();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 76fe3bf..2bc07ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -36,9 +36,9 @@
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tracing.ProtoTracer;
@@ -68,7 +68,7 @@
     @Mock CommandQueue mCommandQueue;
     @Mock ConfigurationController mConfigurationController;
     @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    @Mock ActivityManagerWrapper mActivityManagerWrapper;
+    @Mock TaskStackChangeListeners mTaskStackChangeListeners;
     @Mock DisplayImeController mDisplayImeController;
     @Mock InputConsumerController mMockInputConsumerController;
     @Mock NavigationModeController mNavigationModeController;
@@ -88,7 +88,7 @@
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
 
         mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
-                mInputConsumerController, mKeyguardUpdateMonitor, mActivityManagerWrapper,
+                mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners,
                 mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
                 Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
                 mTaskOrganizer, mProtoTracer);
@@ -116,7 +116,7 @@
         final TestableContext nonPipContext = getNonPipFeatureContext();
         final WMShell nonPipWMShell = new WMShell(nonPipContext, mCommandQueue,
                 mConfigurationController, mMockInputConsumerController, mKeyguardUpdateMonitor,
-                mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
+                mTaskStackChangeListeners, mDisplayImeController, mNavigationModeController,
                 mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
                 Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
         nonPipWMShell.initPip(mPip);
@@ -125,7 +125,7 @@
         verify(mKeyguardUpdateMonitor, never()).registerCallback(any());
         verify(mConfigurationController, never()).addCallback(any());
         verify(mSysUiState, never()).addCallback(any());
-        verify(mActivityManagerWrapper, never()).registerTaskStackListener(any());
+        verify(mTaskStackChangeListeners, never()).registerTaskStackListener(any());
         verify(mMockInputConsumerController, never()).setInputListener(any());
         verify(mMockInputConsumerController, never()).setRegistrationListener(any());
         verify(mPip, never()).registerSessionListenerForCurrentUser();
@@ -136,7 +136,7 @@
         mWMShell.initSplitScreen(mSplitScreen);
 
         verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
-        verify(mActivityManagerWrapper).registerTaskStackListener(
+        verify(mTaskStackChangeListeners).registerTaskStackListener(
                 any(TaskStackChangeListener.class));
     }
 
@@ -149,7 +149,7 @@
         verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
         verify(mNavigationModeController).addListener(
                 any(NavigationModeController.ModeChangedListener.class));
-        verify(mActivityManagerWrapper).registerTaskStackListener(
+        verify(mTaskStackChangeListeners).registerTaskStackListener(
                 any(TaskStackChangeListener.class));
 
         verify(mOneHanded).registerGestureCallback(any(
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 6276c4e..0cf14e3 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -20,6 +20,10 @@
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
 import static android.net.util.PrefixUtils.asIpPrefix;
 
+import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
+import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
+
 import static java.util.Arrays.asList;
 
 import android.content.Context;
@@ -37,9 +41,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Random;
@@ -58,10 +63,6 @@
 public class PrivateAddressCoordinator {
     public static final int PREFIX_LENGTH = 24;
 
-    private static final int MAX_UBYTE = 256;
-    private static final int BYTE_MASK = 0xff;
-    private static final byte DEFAULT_ID = (byte) 42;
-
     // Upstream monitor would be stopped when tethering is down. When tethering restart, downstream
     // address may be requested before coordinator get current upstream notification. To ensure
     // coordinator do not select conflict downstream prefix, mUpstreamPrefixMap would not be cleared
@@ -69,22 +70,22 @@
     // mUpstreamPrefixMap when tethering is starting. See #maybeRemoveDeprecatedUpstreams().
     private final ArrayMap<Network, List<IpPrefix>> mUpstreamPrefixMap;
     private final ArraySet<IpServer> mDownstreams;
-    // IANA has reserved the following three blocks of the IP address space for private intranets:
-    // 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
-    // Tethering use 192.168.0.0/16 that has 256 contiguous class C network numbers.
-    private static final String DEFAULT_TETHERING_PREFIX = "192.168.0.0/16";
     private static final String LEGACY_WIFI_P2P_IFACE_ADDRESS = "192.168.49.1/24";
     private static final String LEGACY_BLUETOOTH_IFACE_ADDRESS = "192.168.44.1/24";
-    private final IpPrefix mTetheringPrefix;
+    private final List<IpPrefix> mTetheringPrefixes;
     private final ConnectivityManager mConnectivityMgr;
     private final TetheringConfiguration mConfig;
     // keyed by downstream type(TetheringManager.TETHERING_*).
     private final SparseArray<LinkAddress> mCachedAddresses;
 
     public PrivateAddressCoordinator(Context context, TetheringConfiguration config) {
+        this(context, config, new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))));
+    }
+
+    public PrivateAddressCoordinator(Context context, TetheringConfiguration config,
+            List<IpPrefix> prefixPools) {
         mDownstreams = new ArraySet<>();
         mUpstreamPrefixMap = new ArrayMap<>();
-        mTetheringPrefix = new IpPrefix(DEFAULT_TETHERING_PREFIX);
         mConnectivityMgr = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         mConfig = config;
@@ -92,6 +93,8 @@
         // Reserved static addresses for bluetooth and wifi p2p.
         mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS));
         mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS));
+
+        mTetheringPrefixes = prefixPools;
     }
 
     /**
@@ -179,52 +182,148 @@
             return cachedAddress;
         }
 
-        // Address would be 192.168.[subAddress]/24.
-        final byte[] bytes = mTetheringPrefix.getRawAddress();
-        final int subAddress = getRandomSubAddr();
-        final int subNet = (subAddress >> 8) & BYTE_MASK;
-        bytes[3] = getSanitizedAddressSuffix(subAddress, (byte) 0, (byte) 1, (byte) 0xff);
-        for (int i = 0; i < MAX_UBYTE; i++) {
-            final int newSubNet = (subNet + i) & BYTE_MASK;
-            bytes[2] = (byte) newSubNet;
-
-            final InetAddress addr;
-            try {
-                addr = InetAddress.getByAddress(bytes);
-            } catch (UnknownHostException e) {
-                throw new IllegalStateException("Invalid address, shouldn't happen.", e);
+        for (IpPrefix prefixRange : mTetheringPrefixes) {
+            final LinkAddress newAddress = chooseDownstreamAddress(prefixRange);
+            if (newAddress != null) {
+                mDownstreams.add(ipServer);
+                mCachedAddresses.put(ipServer.interfaceType(), newAddress);
+                return newAddress;
             }
-
-            if (isConflict(new IpPrefix(addr, PREFIX_LENGTH))) continue;
-
-            mDownstreams.add(ipServer);
-            final LinkAddress newAddress = new LinkAddress(addr, PREFIX_LENGTH);
-            mCachedAddresses.put(ipServer.interfaceType(), newAddress);
-            return newAddress;
         }
 
         // No available address.
         return null;
     }
 
-    private boolean isConflict(final IpPrefix prefix) {
-        // Check whether this prefix is in use or conflict with any current upstream network.
-        return isDownstreamPrefixInUse(prefix) || isConflictWithUpstream(prefix);
+    private int getPrefixBaseAddress(final IpPrefix prefix) {
+        return inet4AddressToIntHTH((Inet4Address) prefix.getAddress());
     }
 
-    /** Get random sub address value. Return value is in 0 ~ 0xffff. */
-    @VisibleForTesting
-    public int getRandomSubAddr() {
-        return ((new Random()).nextInt()) & 0xffff; // subNet is in 0 ~ 0xffff.
+    /**
+     * Check whether input prefix conflict with upstream prefixes or in-use downstream prefixes.
+     * If yes, return one of them.
+     */
+    private IpPrefix getConflictPrefix(final IpPrefix prefix) {
+        final IpPrefix upstream = getConflictWithUpstream(prefix);
+        if (upstream != null) return upstream;
+
+        return getInUseDownstreamPrefix(prefix);
     }
 
-    private byte getSanitizedAddressSuffix(final int source, byte... excluded) {
-        final byte subId = (byte) (source & BYTE_MASK);
-        for (byte value : excluded) {
-            if (subId == value) return DEFAULT_ID;
+    // Get the next non-conflict sub prefix. E.g: To get next sub prefix from 10.0.0.0/8, if the
+    // previously selected prefix is 10.20.42.0/24(subPrefix: 0.20.42.0) and the conflicting prefix
+    // is 10.16.0.0/20 (10.16.0.0 ~ 10.16.15.255), then the max address under subPrefix is
+    // 0.16.15.255 and the next subPrefix is 0.16.16.255/24 (0.16.15.255 + 0.0.1.0).
+    // Note: the sub address 0.0.0.255 here is fine to be any value that it will be replaced as
+    // selected random sub address later.
+    private int getNextSubPrefix(final IpPrefix conflictPrefix, final int prefixRangeMask) {
+        final int suffixMask = ~prefixLengthToV4NetmaskIntHTH(conflictPrefix.getPrefixLength());
+        // The largest offset within the prefix assignment block that still conflicts with
+        // conflictPrefix.
+        final int maxConflict =
+                (getPrefixBaseAddress(conflictPrefix) | suffixMask) & ~prefixRangeMask;
+
+        final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+        // Pick a sub prefix a full prefix (1 << (32 - PREFIX_LENGTH) addresses) greater than
+        // maxConflict. This ensures that the selected prefix never overlaps with conflictPrefix.
+        // There is no need to mask the result with PREFIX_LENGTH bits because this is done by
+        // findAvailablePrefixFromRange when it constructs the prefix.
+        return maxConflict + (1 << (32 - PREFIX_LENGTH));
+    }
+
+    private LinkAddress chooseDownstreamAddress(final IpPrefix prefixRange) {
+        // The netmask of the prefix assignment block (e.g., 0xfff00000 for 172.16.0.0/12).
+        final int prefixRangeMask = prefixLengthToV4NetmaskIntHTH(prefixRange.getPrefixLength());
+
+        // The zero address in the block (e.g., 0xac100000 for 172.16.0.0/12).
+        final int baseAddress = getPrefixBaseAddress(prefixRange);
+
+        // The subnet mask corresponding to PREFIX_LENGTH.
+        final int prefixMask = prefixLengthToV4NetmaskIntHTH(PREFIX_LENGTH);
+
+        // The offset within prefixRange of a randomly-selected prefix of length PREFIX_LENGTH.
+        // This may not be the prefix of the address returned by this method:
+        // - If it is already in use, the method will return an address in another prefix.
+        // - If all prefixes within prefixRange are in use, the method will return null. For
+        // example, for a /24 prefix within 172.26.0.0/12, this will be a multiple of 256 in
+        // [0, 1048576). In other words, a random 32-bit number with mask 0x000fff00.
+        //
+        // prefixRangeMask is required to ensure no wrapping. For example, consider:
+        // - prefixRange 127.0.0.0/8
+        // - randomPrefixStart 127.255.255.0
+        // - A conflicting prefix of 127.255.254.0/23
+        // In this case without prefixRangeMask, getNextSubPrefix would return 128.0.0.0, which
+        // means the "start < end" check in findAvailablePrefixFromRange would not reject the prefix
+        // because Java doesn't have unsigned integers, so 128.0.0.0 = 0x80000000 = -2147483648
+        // is less than 127.0.0.0 = 0x7f000000 = 2130706432.
+        //
+        // Additionally, it makes debug output easier to read by making the numbers smaller.
+        final int randomPrefixStart = getRandomInt() & ~prefixRangeMask & prefixMask;
+
+        // A random offset within the prefix. Used to determine the local address once the prefix
+        // is selected. It does not result in an IPv4 address ending in .0, .1, or .255
+        // For a PREFIX_LENGTH of 255, this is a number between 2 and 254.
+        final int subAddress = getSanitizedSubAddr(~prefixMask);
+
+        // Find a prefix length PREFIX_LENGTH between randomPrefixStart and the end of the block,
+        // such that the prefix does not conflict with any upstream.
+        IpPrefix downstreamPrefix = findAvailablePrefixFromRange(
+                 randomPrefixStart, (~prefixRangeMask) + 1, baseAddress, prefixRangeMask);
+        if (downstreamPrefix != null) return getLinkAddress(downstreamPrefix, subAddress);
+
+        // If that failed, do the same, but between 0 and randomPrefixStart.
+        downstreamPrefix = findAvailablePrefixFromRange(
+                0, randomPrefixStart, baseAddress, prefixRangeMask);
+
+        return getLinkAddress(downstreamPrefix, subAddress);
+    }
+
+    private LinkAddress getLinkAddress(final IpPrefix prefix, final int subAddress) {
+        if (prefix == null) return null;
+
+        final InetAddress address = intToInet4AddressHTH(getPrefixBaseAddress(prefix) | subAddress);
+        return new LinkAddress(address, PREFIX_LENGTH);
+    }
+
+    private IpPrefix findAvailablePrefixFromRange(final int start, final int end,
+            final int baseAddress, final int prefixRangeMask) {
+        int newSubPrefix = start;
+        while (newSubPrefix < end) {
+            final InetAddress address = intToInet4AddressHTH(baseAddress | newSubPrefix);
+            final IpPrefix prefix = new IpPrefix(address, PREFIX_LENGTH);
+
+            final IpPrefix conflictPrefix = getConflictPrefix(prefix);
+
+            if (conflictPrefix == null) return prefix;
+
+            newSubPrefix = getNextSubPrefix(conflictPrefix, prefixRangeMask);
         }
 
-        return subId;
+        return null;
+    }
+
+    /** Get random int which could be used to generate random address. */
+    @VisibleForTesting
+    public int getRandomInt() {
+        return (new Random()).nextInt();
+    }
+
+    /** Get random subAddress and avoid selecting x.x.x.0, x.x.x.1 and x.x.x.255 address. */
+    private int getSanitizedSubAddr(final int subAddrMask) {
+        final int randomSubAddr = getRandomInt() & subAddrMask;
+        // If prefix length > 30, the selecting speace would be less than 4 which may be hard to
+        // avoid 3 consecutive address.
+        if (PREFIX_LENGTH > 30) return randomSubAddr;
+
+        // TODO: maybe it is not necessary to avoid .0, .1 and .255 address because tethering
+        // address would not be conflicted. This code only works because PREFIX_LENGTH is not longer
+        // than 24
+        final int candidate = randomSubAddr & 0xff;
+        if (candidate == 0 || candidate == 1 || candidate == 255) {
+            return (randomSubAddr & 0xfffffffc) + 2;
+        }
+
+        return randomSubAddr;
     }
 
     /** Release downstream record for IpServer. */
@@ -237,14 +336,18 @@
         mUpstreamPrefixMap.clear();
     }
 
-    private boolean isConflictWithUpstream(final IpPrefix source) {
+    private IpPrefix getConflictWithUpstream(final IpPrefix prefix) {
         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
             final List<IpPrefix> list = mUpstreamPrefixMap.valueAt(i);
-            for (IpPrefix target : list) {
-                if (isConflictPrefix(source, target)) return true;
+            for (IpPrefix upstream : list) {
+                if (isConflictPrefix(prefix, upstream)) return upstream;
             }
         }
-        return false;
+        return null;
+    }
+
+    private boolean isConflictWithUpstream(final IpPrefix prefix) {
+        return getConflictWithUpstream(prefix) != null;
     }
 
     private boolean isConflictPrefix(final IpPrefix prefix1, final IpPrefix prefix2) {
@@ -257,11 +360,10 @@
 
     // InUse Prefixes are prefixes of mCachedAddresses which are active downstream addresses, last
     // downstream addresses(reserved for next time) and static addresses(e.g. bluetooth, wifi p2p).
-    private boolean isDownstreamPrefixInUse(final IpPrefix prefix) {
-        // This class always generates downstream prefixes with the same prefix length, so
-        // prefixes cannot be contained in each other. They can only be equal to each other.
+    private IpPrefix getInUseDownstreamPrefix(final IpPrefix prefix) {
         for (int i = 0; i < mCachedAddresses.size(); i++) {
-            if (prefix.equals(asIpPrefix(mCachedAddresses.valueAt(i)))) return true;
+            final IpPrefix downstream = asIpPrefix(mCachedAddresses.valueAt(i));
+            if (isConflictPrefix(prefix, downstream)) return downstream;
         }
 
         // IpServer may use manually-defined address (mStaticIpv4ServerAddr) which does not include
@@ -270,10 +372,10 @@
             final IpPrefix target = getDownstreamPrefix(downstream);
             if (target == null) continue;
 
-            if (isConflictPrefix(prefix, target)) return true;
+            if (isConflictPrefix(prefix, target)) return target;
         }
 
-        return false;
+        return null;
     }
 
     private IpPrefix getDownstreamPrefix(final IpServer downstream) {
@@ -284,6 +386,13 @@
     }
 
     void dump(final IndentingPrintWriter pw) {
+        pw.println("mTetheringPrefixes:");
+        pw.increaseIndent();
+        for (IpPrefix prefix : mTetheringPrefixes) {
+            pw.println(prefix);
+        }
+        pw.decreaseIndent();
+
         pw.println("mUpstreamPrefixMap:");
         pw.increaseIndent();
         for (int i = 0; i < mUpstreamPrefixMap.size(); i++) {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 474f4e8..5a0c5b0 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -326,7 +326,7 @@
         // It is OK for the configuration to be passed to the PrivateAddressCoordinator at
         // construction time because the only part of the configuration it uses is
         // shouldEnableWifiP2pDedicatedIp(), and currently do not support changing that.
-        mPrivateAddressCoordinator = new PrivateAddressCoordinator(mContext, mConfig);
+        mPrivateAddressCoordinator = mDeps.getPrivateAddressCoordinator(mContext, mConfig);
 
         // Must be initialized after tethering configuration is loaded because BpfCoordinator
         // constructor needs to use the configuration.
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 131a5fb..45b9141 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -156,4 +156,12 @@
     public boolean isTetheringDenied() {
         return TextUtils.equals(SystemProperties.get("ro.tether.denied"), "true");
     }
+
+    /**
+     * Get a reference to PrivateAddressCoordinator to be used by Tethering.
+     */
+    public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+            TetheringConfiguration cfg) {
+        return new PrivateAddressCoordinator(ctx, cfg);
+    }
 }
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 747d3e8..42a91aa 100644
--- a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -17,9 +17,9 @@
 package android.net.ip;
 
 import static android.system.OsConstants.IPPROTO_ICMPV6;
-import static android.system.OsConstants.IPPROTO_TCP;
 
-import static com.android.internal.util.BitUtils.uint16;
+import static com.android.net.module.util.IpUtils.icmpv6Checksum;
+import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -30,34 +30,29 @@
 import android.net.INetd;
 import android.net.InetAddresses;
 import android.net.MacAddress;
-import android.net.TestNetworkInterface;
-import android.net.TestNetworkManager;
 import android.net.util.InterfaceParams;
-import android.net.util.IpUtils;
 import android.net.util.TetheringUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
-import android.system.ErrnoException;
-import android.system.Os;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.testutils.TapPacketReader;
+import com.android.testutils.TapPacketReaderRule;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
-import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -65,16 +60,18 @@
     private static final int DATA_BUFFER_LEN = 4096;
     private static final int PACKET_TIMEOUT_MS = 5_000;
 
-    // TODO: make NetworkStackConstants accessible to this test and use the constant from there.
-    private static final int ETHER_SRC_ADDR_OFFSET = 6;
+    // Start the readers manually on a common handler shared with DadProxy, for simplicity
+    @Rule
+    public final TapPacketReaderRule mUpstreamReader = new TapPacketReaderRule(
+            DATA_BUFFER_LEN, false /* autoStart */);
+    @Rule
+    public final TapPacketReaderRule mTetheredReader = new TapPacketReaderRule(
+            DATA_BUFFER_LEN, false /* autoStart */);
 
-    private DadProxy mProxy;
-    TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
     private InterfaceParams mUpstreamParams, mTetheredParams;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
-    private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
 
     private static INetd sNetd;
 
@@ -106,12 +103,12 @@
 
     @After
     public void tearDown() throws Exception {
+        mUpstreamReader.stop();
+        mTetheredReader.stop();
+
         if (mHandlerThread != null) {
-            mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
-            mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
-            mUpstreamTapFd = null;
-            mTetheredTapFd = null;
             mHandlerThread.quitSafely();
+            mHandlerThread.join(PACKET_TIMEOUT_MS);
         }
 
         if (mTetheredParams != null) {
@@ -120,54 +117,20 @@
         if (mUpstreamParams != null) {
             sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
         }
-
-        if (mUpstreamTestIface != null) {
-            try {
-                Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
-            } catch (ErrnoException e) { }
-        }
-
-        if (mTetheredTestIface != null) {
-            try {
-                Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
-            } catch (ErrnoException e) { }
-        }
-    }
-
-    private TestNetworkInterface setupTapInterface() {
-        final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
-        AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
-
-        inst.getUiAutomation().adoptShellPermissionIdentity();
-        try {
-            final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
-                    Context.TEST_NETWORK_SERVICE);
-            iface.set(tnm.createTapInterface());
-        } finally {
-            inst.getUiAutomation().dropShellPermissionIdentity();
-        }
-
-        return iface.get();
     }
 
     private void setupTapInterfaces() {
         // Create upstream test iface.
-        mUpstreamTestIface = setupTapInterface();
-        mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
+        mUpstreamReader.start(mHandler);
+        mUpstreamParams = InterfaceParams.getByName(mUpstreamReader.iface.getInterfaceName());
         assertNotNull(mUpstreamParams);
-        mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
-        mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
-                                                    DATA_BUFFER_LEN);
-        mHandler.post(mUpstreamPacketReader::start);
+        mUpstreamPacketReader = mUpstreamReader.getReader();
 
         // Create tethered test iface.
-        mTetheredTestIface = setupTapInterface();
-        mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
+        mTetheredReader.start(mHandler);
+        mTetheredParams = InterfaceParams.getByName(mTetheredReader.getIface().getInterfaceName());
         assertNotNull(mTetheredParams);
-        mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
-        mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
-                                                    DATA_BUFFER_LEN);
-        mHandler.post(mTetheredPacketReader::start);
+        mTetheredPacketReader = mTetheredReader.getReader();
     }
 
     private static final int IPV6_HEADER_LEN = 40;
@@ -177,31 +140,6 @@
     private static final int ICMPV6_CHECKSUM_OFFSET = 2;
     private static final int ETHER_TYPE_IPV6 = 0x86dd;
 
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static int checksumFold(int sum) {
-        while (sum > 0xffff) {
-            sum = (sum >> 16) + (sum & 0xffff);
-        }
-        return sum;
-    }
-
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static short checksumAdjust(short checksum, short oldWord, short newWord) {
-        checksum = (short) ~checksum;
-        int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
-        return (short) ~tempSum;
-    }
-
-    // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
-    private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
-            int transportLen) {
-        // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
-        // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
-        // checksum adjustment  for the change in the next header byte.
-        short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
-        return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
-    }
-
     private static ByteBuffer createDadPacket(int type) {
         // Refer to buildArpPacket()
         int icmpLen = ICMPV6_NA_NS_LEN
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 191eb6e..86e6f11 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -51,6 +51,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class PrivateAddressCoordinatorTest {
@@ -70,7 +73,17 @@
     private final Network mWifiNetwork = new Network(1);
     private final Network mMobileNetwork = new Network(2);
     private final Network mVpnNetwork = new Network(3);
-    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork};
+    private final Network mMobileNetwork2 = new Network(4);
+    private final Network mMobileNetwork3 = new Network(5);
+    private final Network mMobileNetwork4 = new Network(6);
+    private final Network mMobileNetwork5 = new Network(7);
+    private final Network mMobileNetwork6 = new Network(8);
+    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork,
+            mMobileNetwork2, mMobileNetwork3, mMobileNetwork4, mMobileNetwork5, mMobileNetwork6};
+    private final ArrayList<IpPrefix> mTetheringPrefixes = new ArrayList<>(Arrays.asList(
+            new IpPrefix("192.168.0.0/16"),
+            new IpPrefix("172.16.0.0/12"),
+            new IpPrefix("10.0.0.0/8")));
 
     private void setUpIpServers() throws Exception {
         when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
@@ -87,7 +100,8 @@
         when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks);
         when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false);
         setUpIpServers();
-        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig));
+        mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig,
+                mTetheringPrefixes));
     }
 
     @Test
@@ -117,28 +131,28 @@
     @Test
     public void testSanitizedAddress() throws Exception {
         int fakeSubAddr = 0x2b00; // 43.0.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
         LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, false /* useLastAddress */);
-        assertEquals(new LinkAddress("192.168.43.42/24"), actualAddress);
+        assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
         fakeSubAddr = 0x2d01; // 45.1.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, false /* useLastAddress */);
-        assertEquals(new LinkAddress("192.168.45.42/24"), actualAddress);
+        assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
         fakeSubAddr = 0x2eff; // 46.255.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, false /* useLastAddress */);
-        assertEquals(new LinkAddress("192.168.46.42/24"), actualAddress);
+        assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress);
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
         fakeSubAddr = 0x2f05; // 47.5.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr);
         actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, false /* useLastAddress */);
         assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress);
@@ -148,7 +162,7 @@
     @Test
     public void testReservedPrefix() throws Exception {
         // - Test bluetooth prefix is reserved.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
                 getSubAddress(mBluetoothAddress.getAddress().getAddress()));
         final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, false /* useLastAddress */);
@@ -157,7 +171,7 @@
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
 
         // - Test previous enabled hotspot prefix(cached prefix) is reserved.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
                 getSubAddress(hotspotAddress.getAddress().getAddress()));
         final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mUsbIpServer, false /* useLastAddress */);
@@ -167,7 +181,7 @@
         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
 
         // - Test wifi p2p prefix is reserved.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
                 getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
         final LinkAddress etherAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mEthernetIpServer, false /* useLastAddress */);
@@ -182,7 +196,7 @@
     public void testRequestLastDownstreamAddress() throws Exception {
         final int fakeHotspotSubAddr = 0x2b05;
         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
         final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, true /* useLastAddress */);
         assertEquals("Wrong wifi prefix: ", predefinedPrefix, asIpPrefix(hotspotAddress));
@@ -196,7 +210,7 @@
         mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer);
 
         final int newFakeSubAddr = 0x3c05;
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
 
         final LinkAddress newHotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, true /* useLastAddress */);
@@ -229,11 +243,10 @@
 
     @Test
     public void testNoConflictUpstreamPrefix() throws Exception {
-        final int fakeHotspotSubId = 43;
-        final int fakeHotspotSubAddr = 0x2b05;
+        final int fakeHotspotSubAddr = 0x2b05; // 43.5
         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
         // Force always get subAddress "43.5" for conflict testing.
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr);
         // - Enable hotspot with prefix 192.168.43.0/24
         final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer, true /* useLastAddress */);
@@ -312,6 +325,209 @@
         assertEquals(predefinedPrefix, ethPrefix);
     }
 
+    @Test
+    public void testChooseAvailablePrefix() throws Exception {
+        final int randomAddress = 0x8605; // 134.5
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+        final LinkAddress addr0 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5.
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr0);
+        final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.134.13/26"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+
+        // Check whether return address is next prefix of 192.168.134.0/24.
+        final LinkAddress addr1 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr1);
+        final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.149.16/19"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream2);
+
+
+        // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24.
+        final LinkAddress addr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr2);
+        final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("192.168.129.53/18"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        // Update another conflict upstream which is covered by the previous one (but not the first
+        // one) and verify whether this would affect the result.
+        final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+                new LinkAddress("192.168.170.7/19"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+
+        // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24.
+        final LinkAddress addr3 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr3);
+        final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+                new LinkAddress("192.168.188.133/17"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+
+        // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because
+        // 192.168.134/24 ~ 192.168.255.255/24 is not available.
+        final LinkAddress addr4 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr4);
+        final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+                new LinkAddress("192.168.3.59/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+
+        // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24.
+        final LinkAddress addr5 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr5);
+        final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+                new LinkAddress("192.168.68.43/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+
+        // Update an upstream that does *not* conflict, check whether return the same address
+        // 192.168.5/24.
+        final LinkAddress addr6 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr6);
+        final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6,
+                new LinkAddress("192.168.10.97/21"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6);
+
+        // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24.
+        final LinkAddress addr7 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr7);
+        final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6,
+                new LinkAddress("192.168.0.0/17"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7);
+
+        // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16.
+        final LinkAddress addr8 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8);
+        when(mHotspotIpServer.getAddress()).thenReturn(addr6);
+    }
+
+    @Test
+    public void testChoosePrefixFromDifferentRanges() throws Exception {
+        final int randomAddress = 0x1f2b2a; // 31.43.42
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress);
+        final LinkAddress classC1 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42.
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1);
+        when(mHotspotIpServer.getAddress()).thenReturn(classC1);
+        final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.88.23/17"), null,
+                makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // Check whether return address is next address of prefix 192.168.128.0/17.
+        final LinkAddress classC2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2);
+        when(mHotspotIpServer.getAddress()).thenReturn(classC2);
+        final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("192.1.2.3/8"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // Check whether return address is under prefix 172.16.0.0/12.
+        final LinkAddress classB1 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1);
+        when(mHotspotIpServer.getAddress()).thenReturn(classB1);
+        final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2,
+                new LinkAddress("172.28.123.100/14"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+
+        // 172.28.0.0 ~ 172.31.255.255 is not available.
+        // Check whether return address is next address of prefix 172.16.0.0/14.
+        final LinkAddress classB2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2);
+        when(mHotspotIpServer.getAddress()).thenReturn(classB2);
+
+        // Check whether new downstream is next address of address 172.16.0.42/24.
+        final LinkAddress classB3 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3);
+        when(mUsbIpServer.getAddress()).thenReturn(classB3);
+        final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3,
+                new LinkAddress("172.16.0.1/24"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream3);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+
+        // Check whether return address is next address of prefix 172.16.1.42/24.
+        final LinkAddress classB4 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4);
+        when(mHotspotIpServer.getAddress()).thenReturn(classB4);
+        final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4,
+                new LinkAddress("172.16.0.1/13"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verifyNotifyConflictAndRelease(mUsbIpServer);
+
+        // Check whether return address is next address of prefix 172.16.0.1/13.
+        final LinkAddress classB5 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5);
+        when(mHotspotIpServer.getAddress()).thenReturn(classB5);
+        // Check whether return address is next address of prefix 172.24.0.42/24.
+        final LinkAddress classB6 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6);
+        when(mUsbIpServer.getAddress()).thenReturn(classB6);
+        final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5,
+                new LinkAddress("172.24.0.1/12"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream5);
+        verifyNotifyConflictAndRelease(mHotspotIpServer);
+        verifyNotifyConflictAndRelease(mUsbIpServer);
+
+        // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42.
+        final LinkAddress classA1 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mHotspotIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1);
+        when(mHotspotIpServer.getAddress()).thenReturn(classA1);
+        // Check whether new downstream is next address of address 10.31.43.42/24.
+        final LinkAddress classA2 = mPrivateAddressCoordinator.requestDownstreamAddress(
+                mUsbIpServer, true/* useLastAddress */);
+        assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2);
+    }
+
+    private void verifyNotifyConflictAndRelease(final IpServer ipServer) throws Exception {
+        verify(ipServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        mPrivateAddressCoordinator.releaseDownstream(ipServer);
+        reset(ipServer);
+        setUpIpServers();
+    }
+
     private int getSubAddress(final byte... ipv4Address) {
         assertEquals(4, ipv4Address.length);
 
@@ -330,7 +546,7 @@
 
     @Test
     public void testEnableLegacyWifiP2PAddress() throws Exception {
-        when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(
+        when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(
                 getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress()));
         // No matter #shouldEnableWifiP2pDedicatedIp() is enabled or not, legacy wifi p2p prefix
         // is resevered.
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index df57020..20e94b2 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -24,6 +24,9 @@
 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
 import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -179,6 +182,7 @@
     private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0";
     private static final String TEST_NCM_IFNAME = "test_ncm0";
     private static final String TEST_ETH_IFNAME = "test_eth0";
+    private static final String TEST_BT_IFNAME = "test_pan0";
     private static final String TETHERING_NAME = "Tethering";
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
     private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
@@ -230,6 +234,7 @@
     private TetheringConfiguration mConfig;
     private EntitlementManager mEntitleMgr;
     private OffloadController mOffloadCtrl;
+    private PrivateAddressCoordinator mPrivateAddressCoordinator;
 
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
@@ -446,6 +451,18 @@
         public boolean isTetheringDenied() {
             return false;
         }
+
+
+        @Override
+        public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
+                TetheringConfiguration cfg) {
+            final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList(
+                    new IpPrefix("192.168.0.0/16"),
+                    new IpPrefix("172.16.0.0/12"),
+                    new IpPrefix("10.0.0.0/8")));
+            mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(ctx, cfg, prefixPool));
+            return mPrivateAddressCoordinator;
+        }
     }
 
     private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -1875,27 +1892,36 @@
         sendConfigurationChanged();
     }
 
-    private static UpstreamNetworkState buildV4WifiUpstreamState(final String ipv4Address,
-            final int prefixLength, final Network network) {
+    private static UpstreamNetworkState buildV4UpstreamState(final LinkAddress address,
+            final Network network, final String iface, final int transportType) {
         final LinkProperties prop = new LinkProperties();
-        prop.setInterfaceName(TEST_WIFI_IFNAME);
+        prop.setInterfaceName(iface);
 
-        prop.addLinkAddress(
-                new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address),
-                        prefixLength));
+        prop.addLinkAddress(address);
 
         final NetworkCapabilities capabilities = new NetworkCapabilities()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+                .addTransportType(transportType);
         return new UpstreamNetworkState(prop, capabilities, network);
     }
 
+    private void updateV4Upstream(final LinkAddress ipv4Address, final Network network,
+            final String iface, final int transportType) {
+        final UpstreamNetworkState upstream = buildV4UpstreamState(ipv4Address, network, iface,
+                transportType);
+        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
+                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstream);
+        mLooper.dispatchAll();
+    }
+
     @Test
     public void testHandleIpConflict() throws Exception {
         final Network wifiNetwork = new Network(200);
         final Network[] allNetworks = { wifiNetwork };
         when(mCm.getAllNetworks()).thenReturn(allNetworks);
-        UpstreamNetworkState upstreamNetwork = null;
-        runUsbTethering(upstreamNetwork);
+        runUsbTethering(null);
         final ArgumentCaptor<InterfaceConfigurationParcel> ifaceConfigCaptor =
                 ArgumentCaptor.forClass(InterfaceConfigurationParcel.class);
         verify(mNetd).interfaceSetCfg(ifaceConfigCaptor.capture());
@@ -1903,13 +1929,10 @@
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
                 any(), any());
         reset(mNetd, mUsbManager);
-        upstreamNetwork = buildV4WifiUpstreamState(ipv4Address, 30, wifiNetwork);
-        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
-                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
-                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
-                0,
-                upstreamNetwork);
-        mLooper.dispatchAll();
+
+        // Cause a prefix conflict by assigning a /30 out of the downstream's /24 to the upstream.
+        updateV4Upstream(new LinkAddress(InetAddresses.parseNumericAddress(ipv4Address), 30),
+                wifiNetwork, TEST_WIFI_IFNAME, TRANSPORT_WIFI);
         // verify turn off usb tethering
         verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
         mTethering.interfaceRemoved(TEST_USB_IFNAME);
@@ -1921,9 +1944,10 @@
     @Test
     public void testNoAddressAvailable() throws Exception {
         final Network wifiNetwork = new Network(200);
-        final Network[] allNetworks = { wifiNetwork };
+        final Network btNetwork = new Network(201);
+        final Network mobileNetwork = new Network(202);
+        final Network[] allNetworks = { wifiNetwork, btNetwork, mobileNetwork };
         when(mCm.getAllNetworks()).thenReturn(allNetworks);
-        final String upstreamAddress = "192.168.0.100";
         runUsbTethering(null);
         verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
                 any(), any());
@@ -1940,13 +1964,13 @@
         mLooper.dispatchAll();
         reset(mUsbManager, mEm);
 
-        final UpstreamNetworkState upstreamNetwork = buildV4WifiUpstreamState(
-                upstreamAddress, 16, wifiNetwork);
-        mTetheringDependencies.mUpstreamNetworkMonitorSM.sendMessage(
-                Tethering.TetherMainSM.EVENT_UPSTREAM_CALLBACK,
-                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
-                0,
-                upstreamNetwork);
+        updateV4Upstream(new LinkAddress("192.168.0.100/16"), wifiNetwork, TEST_WIFI_IFNAME,
+                TRANSPORT_WIFI);
+        updateV4Upstream(new LinkAddress("172.16.0.0/12"), btNetwork, TEST_BT_IFNAME,
+                TRANSPORT_BLUETOOTH);
+        updateV4Upstream(new LinkAddress("10.0.0.0/8"), mobileNetwork, TEST_MOBILE_IFNAME,
+                TRANSPORT_CELLULAR);
+
         mLooper.dispatchAll();
         // verify turn off usb tethering
         verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE);
diff --git a/services/Android.bp b/services/Android.bp
index a689236..33205c0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -35,6 +35,7 @@
         ":services.coverage-sources",
         ":services.devicepolicy-sources",
         ":services.midi-sources",
+        ":services.musicsearch-sources",
         ":services.net-sources",
         ":services.print-sources",
         ":services.profcollect-sources",
@@ -78,6 +79,7 @@
         "services.coverage",
         "services.devicepolicy",
         "services.midi",
+        "services.musicsearch",
         "services.net",
         "services.people",
         "services.print",
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6157004..032820d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -391,7 +391,10 @@
 
             checkArgument(getCallingUserId() == userId,
                     "Must be called by either same user or system");
-            mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
+            int callingUid = Binder.getCallingUid();
+            if (mAppOpsManager.checkPackage(callingUid, pkg) != AppOpsManager.MODE_ALLOWED) {
+                throw new SecurityException(pkg + " doesn't belong to uid " + callingUid);
+            }
         }
 
         @Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index addaa65..26c28d5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -109,6 +109,7 @@
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.1-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
+        "android.hardware.biometrics.fingerprint-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b59f7645..bb9f6d2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -140,6 +140,7 @@
 import android.net.util.LinkPropertiesUtils.CompareResult;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
+import android.os.BasicShellCommandHandler;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -156,11 +157,8 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.os.ShellCallback;
-import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -7658,14 +7656,14 @@
     }
 
     @Override
-    public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
-            FileDescriptor err, @NonNull String[] args, ShellCallback callback,
-            @NonNull ResultReceiver resultReceiver) {
-        (new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
+    public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+            @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+            @NonNull String[] args) {
+        return new ShellCmd().exec(this, in.getFileDescriptor(), out.getFileDescriptor(),
+                err.getFileDescriptor(), args);
     }
 
-    private class ShellCmd extends ShellCommand {
-
+    private class ShellCmd extends BasicShellCommandHandler {
         @Override
         public int onCommand(String cmd) {
             if (cmd == null) {
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index 6bc1a57..0f4c94b 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -113,6 +113,7 @@
         JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
         builder.setRequiresDeviceIdle(true);
         builder.setRequiresBatteryNotLow(true);
+        builder.setRequiresCharging(true);
         builder.setMinimumLatency(nextScheduleTime);
         tm.schedule(builder.build());
     }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b6a3e23..8ea71b3 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1623,10 +1623,12 @@
         if (!checkNotifyPermission("notifyDisplayInfoChanged()")) {
             return;
         }
+        String str = "notifyDisplayInfoChanged: PhoneId=" + phoneId + " subId=" + subId
+                + " telephonyDisplayInfo=" + telephonyDisplayInfo;
         if (VDBG) {
-            log("notifyDisplayInfoChanged: PhoneId=" + phoneId
-                    + " subId=" + subId + " telephonyDisplayInfo=" + telephonyDisplayInfo);
+            log(str);
         }
+        mLocalLog.log(str);
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 mTelephonyDisplayInfos[phoneId] = telephonyDisplayInfo;
@@ -2354,10 +2356,10 @@
                 pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
                 pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
                 pw.println("mBarringInfo=" + mBarringInfo.get(i));
+                pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
-
             pw.println("mPhoneCapability=" + mPhoneCapability);
             pw.println("mActiveDataSubId=" + mActiveDataSubId);
             pw.println("mRadioPowerState=" + mRadioPowerState);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 0c34744..afddd65 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -33,7 +33,6 @@
 import android.hardware.input.InputManager;
 import android.hardware.vibrator.IVibrator;
 import android.hardware.vibrator.V1_0.EffectStrength;
-import android.icu.text.DateFormat;
 import android.media.AudioManager;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -80,6 +79,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
@@ -90,6 +90,8 @@
 public class VibratorService extends IVibratorService.Stub
         implements InputManager.InputDeviceListener {
     private static final String TAG = "VibratorService";
+    private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+            new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final boolean DEBUG = false;
     private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
 
@@ -126,7 +128,7 @@
     private final LinkedList<VibrationInfo> mPreviousRingVibrations;
     private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
     private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
-    private final LinkedList<ExternalVibration> mPreviousExternalVibrations;
+    private final LinkedList<VibrationInfo> mPreviousExternalVibrations;
     private final LinkedList<VibrationInfo> mPreviousVibrations;
     private final int mPreviousVibrationsLimit;
     private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -162,7 +164,7 @@
     @GuardedBy("mLock")
     private Vibration mCurrentVibration;
     private int mCurVibUid = -1;
-    private ExternalVibration mCurrentExternalVibration;
+    private ExternalVibrationHolder mCurrentExternalVibration;
     private boolean mVibratorUnderExternalControl;
     private boolean mLowPowerMode;
     @GuardedBy("mLock")
@@ -231,19 +233,12 @@
         void onComplete(long vibrationId);
     }
 
-    /**
-     * Holder for a vibration to be played. This class can be shared with native methods for
-     * hardware callback support.
-     */
+    /** Holder for a {@link VibrationEffect}. */
     private final class Vibration implements IBinder.DeathRecipient {
 
         public final IBinder token;
         // Start time in CLOCK_BOOTTIME base.
         public final long startTime;
-        // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
-        // with other system events, any duration calculations should be done use startTime so as
-        // not to be affected by discontinuities created by RTC adjustments.
-        public final long startTimeDebug;
         public final VibrationAttributes attrs;
         public final long id;
         public final int uid;
@@ -255,18 +250,28 @@
         // The original effect that was requested. Typically these two things differ because
         // the effect was scaled based on the users vibration intensity settings.
         public VibrationEffect originalEffect;
+        // The scale applied to the original effect.
+        public float scale;
+
+        // Start/end times in unix epoch time. Only to be used for debugging purposes and to
+        // correlate with other system events, any duration calculations should be done use
+        // startTime so as not to be affected by discontinuities created by RTC adjustments.
+        private final long mStartTimeDebug;
+        private long mEndTimeDebug;
+        private VibrationInfo.Status mStatus;
 
         private Vibration(IBinder token, VibrationEffect effect,
                 VibrationAttributes attrs, int uid, String opPkg, String reason) {
             this.token = token;
-            this.id = mNextVibrationId.getAndIncrement();
             this.effect = effect;
+            this.id = mNextVibrationId.getAndIncrement();
             this.startTime = SystemClock.elapsedRealtime();
-            this.startTimeDebug = System.currentTimeMillis();
             this.attrs = attrs;
             this.uid = uid;
             this.opPkg = opPkg;
             this.reason = reason;
+            mStartTimeDebug = System.currentTimeMillis();
+            mStatus = VibrationInfo.Status.RUNNING;
         }
 
         @Override
@@ -276,11 +281,24 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Vibration finished because binder died, cleaning up");
                     }
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                 }
             }
         }
 
+        public void end(VibrationInfo.Status status) {
+            if (hasEnded()) {
+                // Vibration already ended, keep first ending status set and ignore this one.
+                return;
+            }
+            mStatus = status;
+            mEndTimeDebug = System.currentTimeMillis();
+        }
+
+        public boolean hasEnded() {
+            return mStatus != VibrationInfo.Status.RUNNING;
+        }
+
         public boolean hasTimeoutLongerThan(long millis) {
             final long duration = effect.getDuration();
             return duration >= 0 && duration > millis;
@@ -308,40 +326,109 @@
 
         public VibrationInfo toInfo() {
             return new VibrationInfo(
-                    startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
+                    mStartTimeDebug, mEndTimeDebug, effect, originalEffect, scale, attrs,
+                    uid, opPkg, reason, mStatus);
         }
     }
 
-    private static class VibrationInfo {
+    /** Holder for a {@link ExternalVibration}. */
+    private final class ExternalVibrationHolder {
+
+        public final ExternalVibration externalVibration;
+        public int scale;
+
         private final long mStartTimeDebug;
+        private long mEndTimeDebug;
+        private VibrationInfo.Status mStatus;
+
+        private ExternalVibrationHolder(ExternalVibration externalVibration) {
+            this.externalVibration = externalVibration;
+            this.scale = SCALE_NONE;
+            mStartTimeDebug = System.currentTimeMillis();
+            mStatus = VibrationInfo.Status.RUNNING;
+        }
+
+        public void end(VibrationInfo.Status status) {
+            if (mStatus != VibrationInfo.Status.RUNNING) {
+                // Vibration already ended, keep first ending status set and ignore this one.
+                return;
+            }
+            mStatus = status;
+            mEndTimeDebug = System.currentTimeMillis();
+        }
+
+        public VibrationInfo toInfo() {
+            return new VibrationInfo(
+                    mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
+                    scale, externalVibration.getVibrationAttributes(),
+                    externalVibration.getUid(), externalVibration.getPackage(),
+                    /* reason= */ null, mStatus);
+        }
+    }
+
+    /** Debug information about vibrations. */
+    private static class VibrationInfo {
+
+        public enum Status {
+            RUNNING,
+            FINISHED,
+            CANCELLED,
+            ERROR_APP_OPS,
+            IGNORED,
+            IGNORED_APP_OPS,
+            IGNORED_BACKGROUND,
+            IGNORED_RINGTONE,
+            IGNORED_UNKNOWN_VIBRATION,
+            IGNORED_UNSUPPORTED,
+            IGNORED_FOR_ALARM,
+            IGNORED_FOR_EXTERNAL,
+            IGNORED_FOR_ONGOING,
+            IGNORED_FOR_POWER,
+            IGNORED_FOR_SETTINGS,
+        }
+
+        private final long mStartTimeDebug;
+        private final long mEndTimeDebug;
         private final VibrationEffect mEffect;
         private final VibrationEffect mOriginalEffect;
+        private final float mScale;
         private final VibrationAttributes mAttrs;
         private final int mUid;
         private final String mOpPkg;
         private final String mReason;
+        private final VibrationInfo.Status mStatus;
 
-        VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
-                String opPkg, String reason) {
+        VibrationInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect,
+                VibrationEffect originalEffect, float scale, VibrationAttributes attrs,
+                int uid, String opPkg, String reason, VibrationInfo.Status status) {
             mStartTimeDebug = startTimeDebug;
+            mEndTimeDebug = endTimeDebug;
             mEffect = effect;
             mOriginalEffect = originalEffect;
+            mScale = scale;
             mAttrs = attrs;
             mUid = uid;
             mOpPkg = opPkg;
             mReason = reason;
+            mStatus = status;
         }
 
         @Override
         public String toString() {
             return new StringBuilder()
                     .append("startTime: ")
-                    .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
+                    .append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
+                    .append(", endTime: ")
+                    .append(mEndTimeDebug == 0 ? null
+                            : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
+                    .append(", status: ")
+                    .append(mStatus.name().toLowerCase())
                     .append(", effect: ")
                     .append(mEffect)
                     .append(", originalEffect: ")
                     .append(mOriginalEffect)
+                    .append(", scale: ")
+                    .append(String.format("%.2f", mScale))
                     .append(", attrs: ")
                     .append(mAttrs)
                     .append(", uid: ")
@@ -354,11 +441,80 @@
         }
 
         void dumpProto(ProtoOutputStream proto, long fieldId) {
-            synchronized (this) {
-                final long token = proto.start(fieldId);
-                proto.write(VibrationProto.START_TIME, mStartTimeDebug);
-                proto.end(token);
+            final long token = proto.start(fieldId);
+            proto.write(VibrationProto.START_TIME, mStartTimeDebug);
+            proto.write(VibrationProto.END_TIME, mEndTimeDebug);
+            proto.write(VibrationProto.STATUS, mStatus.ordinal());
+
+            final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
+            proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
+            proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
+            proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
+            proto.end(attrsToken);
+
+            if (mEffect != null) {
+                dumpEffect(proto, VibrationProto.EFFECT, mEffect);
             }
+            if (mOriginalEffect != null) {
+                dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect);
+            }
+
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
+            final long token = proto.start(fieldId);
+            if (effect instanceof VibrationEffect.OneShot) {
+                dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
+            } else if (effect instanceof VibrationEffect.Waveform) {
+                dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect);
+            } else if (effect instanceof VibrationEffect.Prebaked) {
+                dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect);
+            } else if (effect instanceof VibrationEffect.Composed) {
+                dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect);
+            }
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.OneShot effect) {
+            final long token = proto.start(fieldId);
+            proto.write(OneShotProto.DURATION, (int) effect.getDuration());
+            proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude());
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Waveform effect) {
+            final long token = proto.start(fieldId);
+            for (long timing : effect.getTimings()) {
+                proto.write(WaveformProto.TIMINGS, (int) timing);
+            }
+            for (int amplitude : effect.getAmplitudes()) {
+                proto.write(WaveformProto.AMPLITUDES, amplitude);
+            }
+            proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0);
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Prebaked effect) {
+            final long token = proto.start(fieldId);
+            proto.write(PrebakedProto.EFFECT_ID, effect.getId());
+            proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength());
+            proto.write(PrebakedProto.FALLBACK, effect.shouldFallback());
+            proto.end(token);
+        }
+
+        private void dumpEffect(ProtoOutputStream proto, long fieldId,
+                VibrationEffect.Composed effect) {
+            final long token = proto.start(fieldId);
+            for (PrimitiveEffect primitive : effect.getPrimitiveEffects()) {
+                proto.write(ComposedProto.EFFECT_IDS, primitive.id);
+                proto.write(ComposedProto.EFFECT_SCALES, primitive.scale);
+                proto.write(ComposedProto.DELAYS, primitive.delay);
+            }
+            proto.end(token);
         }
     }
 
@@ -549,7 +705,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "Vibration finished by callback, cleaning up");
                 }
-                doCancelVibrateLocked();
+                doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
             }
         }
     }
@@ -792,6 +948,7 @@
             }
 
             attrs = fixupVibrationAttributes(attrs);
+            Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
 
             // If our current vibration is longer than the new vibration and is the same amplitude,
             // then just let the current one finish.
@@ -808,6 +965,7 @@
                             Slog.d(TAG,
                                     "Ignoring incoming vibration in favor of current vibration");
                         }
+                        endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ONGOING);
                         return;
                     }
                 }
@@ -819,6 +977,7 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
                     }
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_EXTERNAL);
                     return;
                 }
 
@@ -832,24 +991,29 @@
                     if (DEBUG) {
                         Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
                     }
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ALARM);
                     return;
                 }
 
-                Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                         && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
                     Slog.e(TAG, "Ignoring incoming vibration as process with"
                             + " uid= " + uid + " is background,"
                             + " attrs= " + vib.attrs);
+                    endVibrationLocked(vib, VibrationInfo.Status.IGNORED_BACKGROUND);
                     return;
                 }
                 linkVibration(vib);
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                     startVibrationLocked(vib);
-                    addToPreviousVibrationsLocked(vib);
+
+                    if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
+                        // Vibration was unexpectedly ignored: add to list for debugging
+                        endVibrationLocked(vib, VibrationInfo.Status.IGNORED);
+                    }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -868,7 +1032,7 @@
         return effect.getDuration() == Long.MAX_VALUE;
     }
 
-    private void addToPreviousVibrationsLocked(Vibration vib) {
+    private void endVibrationLocked(Vibration vib, VibrationInfo.Status status) {
         final LinkedList<VibrationInfo> previousVibrations;
         if (vib.isRingtone()) {
             previousVibrations = mPreviousRingVibrations;
@@ -883,9 +1047,18 @@
         if (previousVibrations.size() > mPreviousVibrationsLimit) {
             previousVibrations.removeFirst();
         }
+        vib.end(status);
         previousVibrations.addLast(vib.toInfo());
     }
 
+    private void endVibrationLocked(ExternalVibrationHolder vib, VibrationInfo.Status status) {
+        if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
+            mPreviousExternalVibrations.removeFirst();
+        }
+        vib.end(status);
+        mPreviousExternalVibrations.addLast(vib.toInfo());
+    }
+
     @Override // Binder call
     public void cancelVibrate(IBinder token) {
         mContext.enforceCallingOrSelfPermission(
@@ -899,7 +1072,7 @@
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    doCancelVibrateLocked();
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -908,7 +1081,7 @@
     }
 
     @GuardedBy("mLock")
-    private void doCancelVibrateLocked() {
+    private void doCancelVibrateLocked(VibrationInfo.Status status) {
         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
         try {
@@ -917,12 +1090,13 @@
                 mThread = null;
             }
             if (mCurrentExternalVibration != null) {
-                mCurrentExternalVibration.mute();
+                endVibrationLocked(mCurrentExternalVibration, status);
+                mCurrentExternalVibration.externalVibration.mute();
                 mCurrentExternalVibration = null;
                 setVibratorUnderExternalControl(false);
             }
             doVibratorOff();
-            reportFinishVibrationLocked();
+            reportFinishVibrationLocked(status);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -936,7 +1110,7 @@
         synchronized (mLock) {
             // Make sure the vibration is really done. This also reports that the vibration is
             // finished.
-            doCancelVibrateLocked();
+            doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
         }
     }
 
@@ -944,7 +1118,7 @@
     private void startVibrationLocked(final Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
         try {
-            final int intensity = getCurrentIntensityLocked(vib);
+            final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
             if (!shouldVibrate(vib, intensity)) {
                 return;
             }
@@ -959,6 +1133,7 @@
     private void startVibrationInnerLocked(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
         try {
+            // Set current vibration before starting it, so callback will work.
             mCurrentVibration = vib;
             if (vib.effect instanceof VibrationEffect.OneShot) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -971,18 +1146,21 @@
             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorPrebakedEffectLocked(vib);
-            } else if (vib.effect instanceof  VibrationEffect.Composed) {
+            } else if (vib.effect instanceof VibrationEffect.Composed) {
                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 doVibratorComposedEffectLocked(vib);
             } else {
                 Slog.e(TAG, "Unknown vibration type, ignoring");
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNKNOWN_VIBRATION);
+                // The set current vibration is not actually playing, so drop it.
+                mCurrentVibration = null;
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
-    private boolean isAllowedToVibrateLocked(Vibration vib) {
+    private boolean shouldVibrateForPowerModeLocked(Vibration vib) {
         if (!mLowPowerMode) {
             return true;
         }
@@ -993,14 +1171,28 @@
                 || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
     }
 
-    private int getCurrentIntensityLocked(Vibration vib) {
-        if (vib.isRingtone()) {
+    private int getCurrentIntensityLocked(int usageHint) {
+        if (isRingtone(usageHint)) {
             return mRingIntensity;
-        } else if (vib.isNotification()) {
+        } else if (isNotification(usageHint)) {
             return mNotificationIntensity;
-        } else if (vib.isHapticFeedback()) {
+        } else if (isHapticFeedback(usageHint)) {
             return mHapticFeedbackIntensity;
-        } else if (vib.isAlarm()) {
+        } else if (isAlarm(usageHint)) {
+            return Vibrator.VIBRATION_INTENSITY_HIGH;
+        } else {
+            return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        }
+    }
+
+    private int getDefaultIntensity(int usageHint) {
+        if (isRingtone(usageHint)) {
+            return mVibrator.getDefaultRingVibrationIntensity();
+        } else if (isNotification(usageHint)) {
+            return mVibrator.getDefaultNotificationVibrationIntensity();
+        } else if (isHapticFeedback(usageHint)) {
+            return mVibrator.getDefaultHapticFeedbackIntensity();
+        } else if (isAlarm(usageHint)) {
             return Vibrator.VIBRATION_INTENSITY_HIGH;
         } else {
             return Vibrator.VIBRATION_INTENSITY_MEDIUM;
@@ -1019,21 +1211,7 @@
             return;
         }
 
-        final int defaultIntensity;
-        if (vib.isRingtone()) {
-            defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
-        } else if (vib.isNotification()) {
-            defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
-        } else if (vib.isHapticFeedback()) {
-            defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
-        } else if (vib.isAlarm()) {
-            defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
-        } else {
-            // If we don't know what kind of vibration we're playing then just skip scaling for
-            // now.
-            return;
-        }
-
+        final int defaultIntensity = getDefaultIntensity(vib.attrs.getUsage());
         final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
         if (scale == null) {
             // We should have scaling levels for all cases, so not being able to scale because of a
@@ -1045,6 +1223,7 @@
 
         vib.originalEffect = vib.effect;
         vib.effect = vib.effect.resolve(mDefaultVibrationAmplitude).scale(scale.factor);
+        vib.scale = scale.factor;
     }
 
     private boolean shouldVibrateForRingtone() {
@@ -1084,11 +1263,13 @@
     }
 
     private boolean shouldVibrate(Vibration vib, int intensity) {
-        if (!isAllowedToVibrateLocked(vib)) {
+        if (!shouldVibrateForPowerModeLocked(vib)) {
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER);
             return false;
         }
 
         if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS);
             return false;
         }
 
@@ -1096,6 +1277,7 @@
             if (DEBUG) {
                 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
             }
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_RINGTONE);
             return false;
         }
 
@@ -1105,6 +1287,9 @@
                 // We might be getting calls from within system_server, so we don't actually
                 // want to throw a SecurityException here.
                 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+                endVibrationLocked(vib, VibrationInfo.Status.ERROR_APP_OPS);
+            } else {
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_APP_OPS);
             }
             return false;
         }
@@ -1113,10 +1298,11 @@
     }
 
     @GuardedBy("mLock")
-    private void reportFinishVibrationLocked() {
+    private void reportFinishVibrationLocked(VibrationInfo.Status status) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
         try {
             if (mCurrentVibration != null) {
+                endVibrationLocked(mCurrentVibration, status);
                 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
                         mCurrentVibration.opPkg);
                 unlinkVibration(mCurrentVibration);
@@ -1153,7 +1339,7 @@
 
             if (devicesUpdated || lowPowerModeUpdated) {
                 // If the state changes out from under us then just reset.
-                doCancelVibrateLocked();
+                doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
             }
 
             updateAlwaysOnLocked();
@@ -1224,7 +1410,7 @@
     }
 
     private void updateAlwaysOnLocked(int id, Vibration vib) {
-        final int intensity = getCurrentIntensityLocked(vib);
+        final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
         if (!shouldVibrate(vib, intensity)) {
             mNativeWrapper.vibratorAlwaysOnDisable(id);
         } else {
@@ -1345,6 +1531,10 @@
                     return;
                 }
             }
+            endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+            // The set current vibration is not actually playing, so drop it.
+            mCurrentVibration = null;
+
             if (!prebaked.shouldFallback()) {
                 return;
             }
@@ -1355,11 +1545,12 @@
             }
             Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
                     vib.opPkg, vib.reason + " (fallback)");
-            final int intensity = getCurrentIntensityLocked(fallbackVib);
+            // Set current vibration before starting it, so callback will work.
+            mCurrentVibration = fallbackVib;
+            final int intensity = getCurrentIntensityLocked(fallbackVib.attrs.getUsage());
             linkVibration(fallbackVib);
             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
             startVibrationInnerLocked(fallbackVib);
-            return;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -1376,11 +1567,10 @@
                 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
             }
             // Input devices don't support composed effect, so skip trying it with them.
-            if (usingInputDeviceVibrators) {
-                return;
-            }
-
-            if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+            if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
+                endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
+                // The set current vibration is not actually playing, so drop it.
+                mCurrentVibration = null;
                 return;
             }
 
@@ -1489,15 +1679,25 @@
             } else {
                 pw.println("null");
             }
-            pw.print("  mCurrentExternalVibration=" + mCurrentExternalVibration);
+            pw.print("  mCurrentExternalVibration=");
+            if (mCurrentExternalVibration != null) {
+                pw.println(mCurrentExternalVibration.toInfo().toString());
+            } else {
+                pw.println("null");
+            }
             pw.println("  mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
             pw.println("  mIsVibrating=" + mIsVibrating);
-            pw.println("  mVibratorStateListeners Count=" +
-                            mVibratorStateListeners.getRegisteredCallbackCount());
+            pw.println("  mVibratorStateListeners Count="
+                    + mVibratorStateListeners.getRegisteredCallbackCount());
             pw.println("  mLowPowerMode=" + mLowPowerMode);
             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
+            pw.println("  mHapticFeedbackDefaultIntensity="
+                    + mVibrator.getDefaultHapticFeedbackIntensity());
             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
+            pw.println("  mNotificationDefaultIntensity="
+                    + mVibrator.getDefaultNotificationVibrationIntensity());
             pw.println("  mRingIntensity=" + mRingIntensity);
+            pw.println("  mRingDefaultIntensity=" + mVibrator.getDefaultRingVibrationIntensity());
             pw.println("  mSupportedEffects=" + mSupportedEffects);
             pw.println("  mSupportedPrimitives=" + mSupportedPrimitives);
             pw.println();
@@ -1523,8 +1723,8 @@
             }
 
             pw.println("  Previous external vibrations:");
-            for (ExternalVibration vib : mPreviousExternalVibrations) {
-                pw.println("    " + vib);
+            for (VibrationInfo info : mPreviousExternalVibrations) {
+                pw.println("    " + info);
             }
         }
     }
@@ -1535,36 +1735,45 @@
         synchronized (mLock) {
             if (mCurrentVibration != null) {
                 mCurrentVibration.toInfo().dumpProto(proto,
-                    VibratorServiceDumpProto.CURRENT_VIBRATION);
+                        VibratorServiceDumpProto.CURRENT_VIBRATION);
+            }
+            if (mCurrentExternalVibration != null) {
+                mCurrentExternalVibration.toInfo().dumpProto(proto,
+                        VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
             }
             proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating);
             proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
-                mVibratorUnderExternalControl);
+                    mVibratorUnderExternalControl);
             proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
             proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
-                mHapticFeedbackIntensity);
-            proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
-                mNotificationIntensity);
+                    mHapticFeedbackIntensity);
+            proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultHapticFeedbackIntensity());
+            proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY, mNotificationIntensity);
+            proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultNotificationVibrationIntensity());
             proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
+            proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
+                    mVibrator.getDefaultRingVibrationIntensity());
 
             for (VibrationInfo info : mPreviousRingVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousNotificationVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousAlarmVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
             }
 
             for (VibrationInfo info : mPreviousVibrations) {
-                info.dumpProto(proto,
-                    VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
+            }
+
+            for (VibrationInfo info : mPreviousExternalVibrations) {
+                info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
             }
         }
         proto.flush();
@@ -1579,8 +1788,8 @@
 
         VibrateWaveformThread(Vibration vib) {
             mWaveform = (VibrationEffect.Waveform) vib.effect;
-            mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid, vib.opPkg,
-                    vib.reason);
+            mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid,
+                    vib.opPkg, vib.reason);
             mTmpWorkSource.set(vib.uid);
             mWakeLock.setWorkSource(mTmpWorkSource);
         }
@@ -1655,8 +1864,8 @@
                                     // appropriate intervals.
                                     onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
                                             repeat);
-                                    mVibration.effect =
-                                            VibrationEffect.createOneShot(onDuration, amplitude);
+                                    mVibration.effect = VibrationEffect.createOneShot(
+                                            onDuration, amplitude);
                                     doVibratorOn(mVibration);
                                 } else {
                                     doVibratorSetAmplitude(amplitude);
@@ -1827,7 +2036,7 @@
                     if (mCurrentVibration != null
                             && !(mCurrentVibration.isHapticFeedback()
                                 && mCurrentVibration.isFromSystem())) {
-                        doCancelVibrateLocked();
+                        doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
                     }
                 }
             }
@@ -1882,63 +2091,54 @@
 
             int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
             if (mode != AppOpsManager.MODE_ALLOWED) {
+                ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
+                vibHolder.scale = SCALE_MUTE;
                 if (mode == AppOpsManager.MODE_ERRORED) {
                     Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
+                    endVibrationLocked(vibHolder, VibrationInfo.Status.ERROR_APP_OPS);
+                } else {
+                    endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS);
                 }
                 return SCALE_MUTE;
             }
 
             final int scaleLevel;
             synchronized (mLock) {
-                if (!vib.equals(mCurrentExternalVibration)) {
-                    if (mCurrentExternalVibration == null) {
-                        // If we're not under external control right now, then cancel any normal
-                        // vibration that may be playing and ready the vibrator for external
-                        // control.
-                        doCancelVibrateLocked();
-                        setVibratorUnderExternalControl(true);
-                    }
-                    // At this point we either have an externally controlled vibration playing, or
-                    // no vibration playing. Since the interface defines that only one externally
-                    // controlled vibration can play at a time, by returning something other than
-                    // SCALE_MUTE from this function we can be assured that if we are currently
-                    // playing vibration, it will be muted in favor of the new vibration.
-                    //
-                    // Note that this doesn't support multiple concurrent external controls, as we
-                    // would need to mute the old one still if it came from a different controller.
-                    mCurrentExternalVibration = vib;
-                    mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
-                    mCurrentExternalVibration.linkToDeath(mCurrentExternalDeathRecipient);
-                    if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
-                        mPreviousExternalVibrations.removeFirst();
-                    }
-                    mPreviousExternalVibrations.addLast(vib);
-                    if (DEBUG) {
-                        Slog.e(TAG, "Playing external vibration: " + vib);
-                    }
+                if (mCurrentExternalVibration != null
+                        && mCurrentExternalVibration.externalVibration.equals(vib)) {
+                    // We are already playing this external vibration, so we can return the same
+                    // scale calculated in the previous call to this method.
+                    return mCurrentExternalVibration.scale;
                 }
-                final int usage = vib.getVibrationAttributes().getUsage();
-                final int defaultIntensity;
-                final int currentIntensity;
-                if (isRingtone(usage)) {
-                    defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
-                    currentIntensity = mRingIntensity;
-                } else if (isNotification(usage)) {
-                    defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
-                    currentIntensity = mNotificationIntensity;
-                } else if (isHapticFeedback(usage)) {
-                    defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
-                    currentIntensity = mHapticFeedbackIntensity;
-                } else if (isAlarm(usage)) {
-                    defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
-                    currentIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
+                if (mCurrentExternalVibration == null) {
+                    // If we're not under external control right now, then cancel any normal
+                    // vibration that may be playing and ready the vibrator for external control.
+                    doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
+                    setVibratorUnderExternalControl(true);
                 } else {
-                    defaultIntensity = 0;
-                    currentIntensity = 0;
+                    endVibrationLocked(mCurrentExternalVibration, VibrationInfo.Status.CANCELLED);
                 }
+                // At this point we either have an externally controlled vibration playing, or
+                // no vibration playing. Since the interface defines that only one externally
+                // controlled vibration can play at a time, by returning something other than
+                // SCALE_MUTE from this function we can be assured that if we are currently
+                // playing vibration, it will be muted in favor of the new vibration.
+                //
+                // Note that this doesn't support multiple concurrent external controls, as we
+                // would need to mute the old one still if it came from a different controller.
+                mCurrentExternalVibration = new ExternalVibrationHolder(vib);
+                mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
+                vib.linkToDeath(mCurrentExternalDeathRecipient);
+                if (DEBUG) {
+                    Slog.e(TAG, "Playing external vibration: " + vib);
+                }
+                int usage = vib.getVibrationAttributes().getUsage();
+                int defaultIntensity = getDefaultIntensity(usage);
+                int currentIntensity = getCurrentIntensityLocked(usage);
                 scaleLevel = currentIntensity - defaultIntensity;
             }
             if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
+                mCurrentExternalVibration.scale = scaleLevel;
                 return scaleLevel;
             } else {
                 // Presumably we want to play this but something about our scaling has gone
@@ -1952,22 +2152,42 @@
         @Override
         public void onExternalVibrationStop(ExternalVibration vib) {
             synchronized (mLock) {
-                if (vib.equals(mCurrentExternalVibration)) {
-                    mCurrentExternalVibration.unlinkToDeath(mCurrentExternalDeathRecipient);
-                    mCurrentExternalDeathRecipient = null;
-                    mCurrentExternalVibration = null;
-                    setVibratorUnderExternalControl(false);
+                if (mCurrentExternalVibration != null
+                        && mCurrentExternalVibration.externalVibration.equals(vib)) {
                     if (DEBUG) {
                         Slog.e(TAG, "Stopping external vibration" + vib);
                     }
+                    doCancelExternalVibrateLocked(VibrationInfo.Status.FINISHED);
                 }
             }
         }
 
+        private void doCancelExternalVibrateLocked(VibrationInfo.Status status) {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
+            try {
+                if (mCurrentExternalVibration == null) {
+                    return;
+                }
+                endVibrationLocked(mCurrentExternalVibration, status);
+                mCurrentExternalVibration.externalVibration.unlinkToDeath(
+                        mCurrentExternalDeathRecipient);
+                mCurrentExternalDeathRecipient = null;
+                mCurrentExternalVibration = null;
+                setVibratorUnderExternalControl(false);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
+        }
+
         private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
             public void binderDied() {
                 synchronized (mLock) {
-                    onExternalVibrationStop(mCurrentExternalVibration);
+                    if (mCurrentExternalVibration != null) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "External vibration finished because binder died");
+                        }
+                        doCancelExternalVibrateLocked(VibrationInfo.Status.CANCELLED);
+                    }
                 }
             }
         }
@@ -2229,5 +2449,4 @@
             }
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b2e021f..0f095ab 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -558,11 +558,18 @@
                 }
                 if (r.mAllowStartForeground == FGS_FEATURE_DENIED
                         && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
-                    Slog.w(TAG, "startForegroundService() not allowed due to "
-                                    + "mAllowStartForeground false: service "
-                                    + r.shortInstanceName);
-                    showFgsBgRestrictedNotificationLocked(r);
-                    forcedStandby = true;
+                    if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+                            && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+                        // uid is on DeviceIdleController's allowlist.
+                        Slog.d(TAG, "startForegroundService() mAllowStartForeground false "
+                                + "but allowlist true: service " + r.shortInstanceName);
+                    } else {
+                        Slog.w(TAG, "startForegroundService() not allowed due to "
+                                + "mAllowStartForeground false: service "
+                                + r.shortInstanceName);
+                        showFgsBgRestrictedNotificationLocked(r);
+                        return null;
+                    }
                 }
             }
         }
@@ -1462,13 +1469,20 @@
                         }
                         if (r.mAllowStartForeground == FGS_FEATURE_DENIED
                                 && mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
-                            Slog.w(TAG,
-                                    "Service.startForeground() not allowed due to "
-                                            + "mAllowStartForeground false: service "
-                                            + r.shortInstanceName);
-                            showFgsBgRestrictedNotificationLocked(r);
-                            updateServiceForegroundLocked(r.app, true);
-                            ignoreForeground = true;
+                            if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled
+                                    && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) {
+                                // uid is on DeviceIdleController's allowlist.
+                                Slog.d(TAG, "Service.startForeground() "
+                                        + "mAllowStartForeground false but allowlist true: service "
+                                        + r.shortInstanceName);
+                            } else {
+                                Slog.w(TAG, "Service.startForeground() not allowed due to "
+                                                + "mAllowStartForeground false: service "
+                                                + r.shortInstanceName);
+                                showFgsBgRestrictedNotificationLocked(r);
+                                updateServiceForegroundLocked(r.app, true);
+                                ignoreForeground = true;
+                            }
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 48055b5..b54a917e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -161,6 +161,13 @@
     private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED =
             "default_fgs_starts_restriction_enabled";
 
+    /**
+     * Default value for mFlagFgsStartTempAllowListEnabled if not explicitly set in
+     * Settings.Global.
+     */
+    private static final String KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED =
+            "default_fgs_starts_temp_allowlist_enabled";
+
     // Maximum number of cached processes we will allow.
     public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
 
@@ -321,6 +328,10 @@
     // at all.
     volatile boolean mFlagFgsStartRestrictionEnabled = false;
 
+    // When the foreground service background start restriction is enabled, if the app in
+    // DeviceIdleController's Temp AllowList is allowed to bypass the restriction.
+    volatile boolean mFlagFgsStartTempAllowListEnabled = false;
+
     private final ActivityManagerService mService;
     private ContentResolver mResolver;
     private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -472,6 +483,9 @@
                             case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
                                 updateFgsStartsRestriction();
                                 break;
+                            case KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED:
+                                updateFgsStartsTempAllowList();
+                                break;
                             case KEY_OOMADJ_UPDATE_POLICY:
                                 updateOomAdjUpdatePolicy();
                                 break;
@@ -724,6 +738,13 @@
                 /*defaultValue*/ false);
     }
 
+    private void updateFgsStartsTempAllowList() {
+        mFlagFgsStartTempAllowListEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_DEFAULT_FGS_STARTS_TEMP_ALLOWLIST_ENABLED,
+                /*defaultValue*/ false);
+    }
+
     private void updateOomAdjUpdatePolicy() {
         OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4e9d456..9afda8c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12127,6 +12127,7 @@
         app.setHasClientActivities(false);
 
         mServices.killServicesLocked(app, allowRestart);
+        mPhantomProcessList.onAppDied(app.pid);
 
         boolean restart = false;
 
@@ -15789,6 +15790,11 @@
         }
 
         @Override
+        public int getPendingIntentFlags(IIntentSender target) {
+            return mPendingIntentController.getPendingIntentFlags(target);
+        }
+
+        @Override
         public void setPendingIntentAllowBgActivityStarts(IIntentSender target,
                 IBinder whitelistToken, int flags) {
             if (!(target instanceof PendingIntentRecord)) {
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index c62df56..2ae3d35 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -311,6 +311,16 @@
         }
     }
 
+    int getPendingIntentFlags(IIntentSender target) {
+        if (!(target instanceof PendingIntentRecord)) {
+            Slog.w(TAG, "markAsSentFromNotification(): not a PendingIntentRecord: " + target);
+            return 0;
+        }
+        synchronized (mLock) {
+            return ((PendingIntentRecord) target).key.flags;
+        }
+    }
+
     private void makeIntentSenderCanceled(PendingIntentRecord rec) {
         rec.canceled = true;
         final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index e2fcf08..5167c57 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -27,15 +27,22 @@
 import android.app.ApplicationExitInfo.SubReason;
 import android.os.Handler;
 import android.os.Process;
+import android.os.StrictMode;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ProcStatsUtil;
 import com.android.internal.os.ProcessCpuTracker;
 
 import libcore.io.IoUtils;
 
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -78,18 +85,178 @@
     @GuardedBy("mLock")
     private final ArrayList<PhantomProcessRecord> mTempPhantomProcesses = new ArrayList<>();
 
+    /**
+     * The mapping between a phantom process ID to its parent process (an app process)
+     */
+    @GuardedBy("mLock")
+    private final SparseArray<ProcessRecord> mPhantomToAppProcessMap = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final SparseArray<InputStream> mCgroupProcsFds = new SparseArray<>();
+
+    @GuardedBy("mLock")
+    private final byte[] mDataBuffer = new byte[4096];
+
     @GuardedBy("mLock")
     private boolean mTrimPhantomProcessScheduled = false;
 
     @GuardedBy("mLock")
     int mUpdateSeq;
 
+    @VisibleForTesting
+    Injector mInjector;
+
     private final ActivityManagerService mService;
     private final Handler mKillHandler;
 
     PhantomProcessList(final ActivityManagerService service) {
         mService = service;
         mKillHandler = service.mProcessList.sKillHandler;
+        mInjector = new Injector();
+    }
+
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    void lookForPhantomProcessesLocked() {
+        mPhantomToAppProcessMap.clear();
+        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+        try {
+            synchronized (mService.mPidsSelfLocked) {
+                for (int i = mService.mPidsSelfLocked.size() - 1; i >= 0; i--) {
+                    final ProcessRecord app = mService.mPidsSelfLocked.valueAt(i);
+                    lookForPhantomProcessesLocked(app);
+                }
+            }
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
+    }
+
+    @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+    private void lookForPhantomProcessesLocked(ProcessRecord app) {
+        if (app.appZygote || app.killed || app.killedByAm) {
+            // process forked from app zygote doesn't have its own acct entry
+            return;
+        }
+        InputStream input = mCgroupProcsFds.get(app.pid);
+        if (input == null) {
+            final String path = getCgroupFilePath(app.info.uid, app.pid);
+            try {
+                input = mInjector.openCgroupProcs(path);
+            } catch (FileNotFoundException | SecurityException e) {
+                if (DEBUG_PROCESSES) {
+                    Slog.w(TAG, "Unable to open " + path, e);
+                }
+                return;
+            }
+            // Keep the FD open for better performance
+            mCgroupProcsFds.put(app.pid, input);
+        }
+        final byte[] buf = mDataBuffer;
+        try {
+            int read = 0;
+            int pid = 0;
+            long totalRead = 0;
+            do {
+                read = mInjector.readCgroupProcs(input, buf, 0, buf.length);
+                if (read == -1) {
+                    break;
+                }
+                totalRead += read;
+                for (int i = 0; i < read; i++) {
+                    final byte b = buf[i];
+                    if (b == '\n') {
+                        addChildPidLocked(app, pid);
+                        pid = 0;
+                    } else {
+                        pid = pid * 10 + (b - '0');
+                    }
+                }
+                if (read < buf.length) {
+                    // we may break from here safely as sysfs reading should return the whole page
+                    // if the remaining data is larger than a page
+                    break;
+                }
+            } while (true);
+            if (pid != 0) {
+                addChildPidLocked(app, pid);
+            }
+            // rewind the fd for the next read
+            input.skip(-totalRead);
+        } catch (IOException e) {
+            Slog.e(TAG, "Error in reading cgroup procs from " + app, e);
+            IoUtils.closeQuietly(input);
+            mCgroupProcsFds.delete(app.pid);
+        }
+    }
+
+    @VisibleForTesting
+    static String getCgroupFilePath(int uid, int pid) {
+        return "/acct/uid_" + uid + "/pid_" + pid + "/cgroup.procs";
+    }
+
+    static String getProcessName(int pid) {
+        String procName = ProcStatsUtil.readTerminatedProcFile(
+                "/proc/" + pid + "/cmdline", (byte) '\0');
+        if (procName == null) {
+            return null;
+        }
+        int l = procName.lastIndexOf('/');
+        if (l > 0 && l < procName.length() - 1) {
+            procName = procName.substring(l + 1);
+        }
+        return procName;
+    }
+
+    @GuardedBy({"mLock", "mService.mPidsSelfLocked"})
+    private void addChildPidLocked(final ProcessRecord app, final int pid) {
+        if (app.pid != pid) {
+            // That's something else...
+            final ProcessRecord r = mService.mPidsSelfLocked.get(pid);
+            if (r != null) {
+                // Is this a process forked via app zygote?
+                if (!r.appZygote) {
+                    // Unexpected...
+                    if (DEBUG_PROCESSES) {
+                        Slog.w(TAG, "Unexpected: " + r + " appears in the cgroup.procs of " + app);
+                    }
+                } else {
+                    // Just a child process of app zygote, no worries
+                }
+            } else {
+                final int index = mPhantomToAppProcessMap.indexOfKey(pid);
+                if (index >= 0) { // unlikely since we cleared the map at the beginning
+                    final ProcessRecord current = mPhantomToAppProcessMap.valueAt(index);
+                    if (app == current) {
+                        // Okay it's unchanged
+                        return;
+                    }
+                    mPhantomToAppProcessMap.setValueAt(index, app);
+                } else {
+                    mPhantomToAppProcessMap.put(pid, app);
+                }
+                // Its UID isn't necessarily to be the same as the app.info.uid, since it could be
+                // forked from child processes of app zygote
+                final int uid = Process.getUidForPid(pid);
+                String procName = mInjector.getProcessName(pid);
+                if (procName == null || uid < 0) {
+                    mPhantomToAppProcessMap.delete(pid);
+                    return;
+                }
+                getOrCreatePhantomProcessIfNeededLocked(procName, uid, pid, true);
+            }
+        }
+    }
+
+    void onAppDied(final int pid) {
+        synchronized (mLock) {
+            final int index = mCgroupProcsFds.indexOfKey(pid);
+            if (index >= 0) {
+                final InputStream inputStream = mCgroupProcsFds.valueAt(index);
+                mCgroupProcsFds.removeAt(index);
+                IoUtils.closeQuietly(inputStream);
+            }
+        }
     }
 
     /**
@@ -99,7 +266,7 @@
      */
     @GuardedBy("mLock")
     PhantomProcessRecord getOrCreatePhantomProcessIfNeededLocked(final String processName,
-            final int uid, final int pid) {
+            final int uid, final int pid, boolean createIfNeeded) {
         // First check if it's actually an app process we know
         if (isAppProcess(pid)) {
             return null;
@@ -123,56 +290,47 @@
                 if (proc.equals(processName, uid, pid)) {
                     return proc;
                 }
-                // Our zombie process information is outdated, let's remove this one, it shoud
+                // Our zombie process information is outdated, let's remove this one, it should
                 // have been gone.
                 mZombiePhantomProcesses.removeAt(idx);
             }
         }
 
-        int ppid = getParentPid(pid);
+        if (!createIfNeeded) {
+            return null;
+        }
 
-        // Walk through its parents and see if it could be traced back to an app process.
-        while (ppid > 1) {
-            if (isAppProcess(ppid)) {
-                // It's a phantom process, bookkeep it
-                try {
-                    final PhantomProcessRecord proc = new PhantomProcessRecord(
-                            processName, uid, pid, ppid, mService,
-                            this::onPhantomProcessKilledLocked);
-                    proc.mUpdateSeq = mUpdateSeq;
-                    mPhantomProcesses.put(pid, proc);
-                    SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(ppid);
-                    if (array == null) {
-                        array = new SparseArray<>();
-                        mAppPhantomProcessMap.put(ppid, array);
-                    }
-                    array.put(pid, proc);
-                    if (proc.mPidFd != null) {
-                        mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
-                                proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
-                                this::onPhantomProcessFdEvent);
-                        mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
-                    }
-                    scheduleTrimPhantomProcessesLocked();
-                    return proc;
-                } catch (IllegalStateException e) {
-                    return null;
+        final ProcessRecord r = mPhantomToAppProcessMap.get(pid);
+
+        if (r != null) {
+            // It's a phantom process, bookkeep it
+            try {
+                final PhantomProcessRecord proc = new PhantomProcessRecord(
+                        processName, uid, pid, r.pid, mService,
+                        this::onPhantomProcessKilledLocked);
+                proc.mUpdateSeq = mUpdateSeq;
+                mPhantomProcesses.put(pid, proc);
+                SparseArray<PhantomProcessRecord> array = mAppPhantomProcessMap.get(r.pid);
+                if (array == null) {
+                    array = new SparseArray<>();
+                    mAppPhantomProcessMap.put(r.pid, array);
                 }
+                array.put(pid, proc);
+                if (proc.mPidFd != null) {
+                    mKillHandler.getLooper().getQueue().addOnFileDescriptorEventListener(
+                            proc.mPidFd, EVENT_INPUT | EVENT_ERROR,
+                            this::onPhantomProcessFdEvent);
+                    mPhantomProcessesPidFds.put(proc.mPidFd.getInt$(), proc);
+                }
+                scheduleTrimPhantomProcessesLocked();
+                return proc;
+            } catch (IllegalStateException e) {
+                return null;
             }
-
-            ppid = getParentPid(ppid);
         }
         return null;
     }
 
-    private static int getParentPid(int pid) {
-        try {
-            return Process.getParentPid(pid);
-        } catch (Exception e) {
-        }
-        return -1;
-    }
-
     private boolean isAppProcess(int pid) {
         synchronized (mService.mPidsSelfLocked) {
             return mService.mPidsSelfLocked.get(pid) != null;
@@ -346,10 +504,14 @@
         synchronized (mLock) {
             // refresh the phantom process list with the latest cpu stats results.
             mUpdateSeq++;
+
+            // Scan app process's accounting procs
+            lookForPhantomProcessesLocked();
+
             for (int i = tracker.countStats() - 1; i >= 0; i--) {
                 final ProcessCpuTracker.Stats st = tracker.getStats(i);
                 final PhantomProcessRecord r =
-                        getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid);
+                        getOrCreatePhantomProcessIfNeededLocked(st.name, st.uid, st.pid, false);
                 if (r != null) {
                     r.mUpdateSeq = mUpdateSeq;
                     r.mCurrentCputime += st.rel_utime + st.rel_stime;
@@ -392,4 +554,19 @@
             proc.dump(pw, prefix + "    ");
         }
     }
+
+    @VisibleForTesting
+    static class Injector {
+        InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+            return new FileInputStream(path);
+        }
+
+        int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+            return input.read(buf, offset, len);
+        }
+
+        String getProcessName(final int pid) {
+            return PhantomProcessList.getProcessName(pid);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 0d077c6..68cfc23 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -26,6 +26,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.attention.AttentionManagerInternal;
 import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
 import android.content.BroadcastReceiver;
@@ -68,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * An attention service implementation that runs in System Server process.
@@ -81,21 +83,15 @@
     /** Service will unbind if connection is not used for that amount of time. */
     private static final long CONNECTION_TTL_MILLIS = 60_000;
 
-    /**
-     * We cache the DeviceConfig values to avoid frequent ashmem-related checks; if the cached
-     * values are stale for more than this duration we will update the cache.
-     */
-    @VisibleForTesting static final long DEVICE_CONFIG_MAX_STALENESS_MILLIS = 4 * 60 * 60 * 1000L;
-
-    @VisibleForTesting long mLastReadDeviceConfigMillis = Long.MIN_VALUE;
-
     /** DeviceConfig flag name, if {@code true}, enables AttentionManagerService features. */
-    private static final String KEY_SERVICE_ENABLED = "service_enabled";
+    @VisibleForTesting
+    static final String KEY_SERVICE_ENABLED = "service_enabled";
 
     /** Default value in absence of {@link DeviceConfig} override. */
     private static final boolean DEFAULT_SERVICE_ENABLED = true;
 
-    private boolean mIsServiceEnabledCached;
+    @VisibleForTesting
+    boolean mIsServiceEnabled;
 
     /**
      * DeviceConfig flag name, describes how much time we consider a result fresh; if the check
@@ -108,7 +104,8 @@
     @VisibleForTesting
     static final long DEFAULT_STALE_AFTER_MILLIS = 1_000;
 
-    private long mStaleAfterMillisCached;
+    @VisibleForTesting
+    long mStaleAfterMillis;
 
     /** The size of the buffer that stores recent attention check results. */
     @VisibleForTesting
@@ -156,6 +153,11 @@
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             mContext.registerReceiver(new ScreenStateReceiver(),
                     new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+            readValuesFromDeviceConfig();
+            DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
+                    ActivityThread.currentApplication().getMainExecutor(),
+                    (properties) -> onDeviceConfigChange(properties.getKeyset()));
         }
     }
 
@@ -179,17 +181,9 @@
         return mComponentName != null;
     }
 
-    /**
-     * Returns {@code true} if attention service is supported on this device.
-     */
-    @VisibleForTesting
-    protected boolean isAttentionServiceSupported() {
-        return isServiceEnabled();
-    }
-
-    private boolean isServiceEnabled() {
-        ensureDeviceConfigCachedValuesFreshness();
-        return mIsServiceEnabledCached;
+    private boolean getIsServiceEnabled() {
+        return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED,
+                DEFAULT_SERVICE_ENABLED);
     }
 
     /**
@@ -198,39 +192,38 @@
      */
     @VisibleForTesting
     protected long getStaleAfterMillis() {
-        ensureDeviceConfigCachedValuesFreshness();
-        return mStaleAfterMillisCached;
-    }
-
-    @VisibleForTesting
-    protected void ensureDeviceConfigCachedValuesFreshness() {
-        final long now = SystemClock.elapsedRealtime();
-        final long whenBecomesStale =
-                mLastReadDeviceConfigMillis + DEVICE_CONFIG_MAX_STALENESS_MILLIS;
-        if (now < whenBecomesStale) {
-            if (DEBUG) {
-                Slog.d(LOG_TAG,
-                        "Cached values are still fresh. Refreshed at=" + mLastReadDeviceConfigMillis
-                                + ", now=" + now);
-            }
-            return;
-        }
-
-        mIsServiceEnabledCached = DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                KEY_SERVICE_ENABLED,
-                DEFAULT_SERVICE_ENABLED);
-
         final long millis = DeviceConfig.getLong(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_STALE_AFTER_MILLIS,
                 DEFAULT_STALE_AFTER_MILLIS);
+
         if (millis < 0 || millis > 10_000) {
             Slog.w(LOG_TAG, "Bad flag value supplied for: " + KEY_STALE_AFTER_MILLIS);
-            mStaleAfterMillisCached = DEFAULT_STALE_AFTER_MILLIS;
-        } else {
-            mStaleAfterMillisCached = millis;
+            return DEFAULT_STALE_AFTER_MILLIS;
         }
 
-        mLastReadDeviceConfigMillis = now;
+        return millis;
+    }
+
+    private void onDeviceConfigChange(@NonNull Set<String> keys) {
+        for (String key : keys) {
+            switch (key) {
+                case KEY_SERVICE_ENABLED:
+                case KEY_STALE_AFTER_MILLIS:
+                    readValuesFromDeviceConfig();
+                    return;
+                default:
+                    Slog.i(LOG_TAG, "Ignoring change on " + key);
+            }
+        }
+    }
+
+    private void readValuesFromDeviceConfig() {
+        mIsServiceEnabled = getIsServiceEnabled();
+        mStaleAfterMillis = getStaleAfterMillis();
+
+        Slog.i(LOG_TAG, "readValuesFromDeviceConfig():"
+                + "\nmIsServiceEnabled=" + mIsServiceEnabled
+                + "\nmStaleAfterMillis=" + mStaleAfterMillis);
     }
 
     /**
@@ -246,7 +239,7 @@
     boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
         Objects.requireNonNull(callbackInternal);
 
-        if (!isAttentionServiceSupported()) {
+        if (!mIsServiceEnabled) {
             Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
             return false;
         }
@@ -272,7 +265,7 @@
             // throttle frequent requests
             final AttentionCheckCache cache = mAttentionCheckCacheBuffer == null ? null
                     : mAttentionCheckCacheBuffer.getLast();
-            if (cache != null && now < cache.mLastComputed + getStaleAfterMillis()) {
+            if (cache != null && now < cache.mLastComputed + mStaleAfterMillis) {
                 callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
                 return true;
             }
@@ -379,7 +372,8 @@
 
     private void dumpInternal(IndentingPrintWriter ipw) {
         ipw.println("Attention Manager Service (dumpsys attention) state:\n");
-        ipw.println("isServiceEnabled=" + isServiceEnabled());
+        ipw.println("isServiceEnabled=" + mIsServiceEnabled);
+        ipw.println("mStaleAfterMillis=" + mStaleAfterMillis);
         ipw.println("AttentionServicePackageName=" + getServiceConfigPackage(mContext));
         ipw.println("Resolved component:");
         if (mComponentName != null) {
@@ -403,7 +397,7 @@
     private final class LocalService extends AttentionManagerInternal {
         @Override
         public boolean isAttentionServiceSupported() {
-            return AttentionManagerService.this.isAttentionServiceSupported();
+            return AttentionManagerService.this.mIsServiceEnabled;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1615998..3b407f1 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -25,6 +25,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
@@ -39,6 +41,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PrintWriterPrinter;
@@ -46,8 +49,8 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Set;
@@ -73,10 +76,6 @@
 
     /** Forced device usage for communications sent to AudioSystem */
     private int mForcedUseForComm;
-    /**
-     * Externally reported force device usage state returned by getters: always consistent
-     * with requests by setters */
-    private int mForcedUseForCommExt;
 
     // Manages all connected devices, only ever accessed on the message loop
     private final AudioDeviceInventory mDeviceInventory;
@@ -136,7 +135,6 @@
         setupMessaging(mContext);
 
         mForcedUseForComm = AudioSystem.FORCE_NONE;
-        mForcedUseForCommExt = mForcedUseForComm;
     }
 
     /*package*/ Context getContext() {
@@ -159,15 +157,6 @@
     }
 
     /*package*/ void onAudioServerDied() {
-        // Restore forced usage for communications and record
-        synchronized (mDeviceStateLock) {
-            AudioSystem.setParameters(
-                    "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
-            onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-            onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm,
-                          false /*fromA2dp*/, "onAudioServerDied");
-        }
         // restore devices
         sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
     }
@@ -219,85 +208,156 @@
      * Turns speakerphone on/off
      * @param on
      * @param eventSource for logging purposes
-     * @return true if speakerphone state changed
      */
-    /*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
-        synchronized (mDeviceStateLock) {
-            if (!addSpeakerphoneClient(cb, pid, on)) {
-                return false;
+    /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device = null;
+                if (on) {
+                    device = new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+                            AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+                } else {
+                    CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                    if (client == null || !client.requestsSpeakerphone()) {
+                        return;
+                    }
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
             }
-            if (on) {
-                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
-                // are mutually exclusive.
-                // See symmetrical operation for startBluetoothScoForClient_Sync().
-                mBtHelper.stopBluetoothScoForPid(pid);
-            }
-            final boolean wasOn = isSpeakerphoneOn();
-            updateSpeakerphoneOn(eventSource);
-            return (wasOn != isSpeakerphoneOn());
         }
     }
 
-    /**
-     * Turns speakerphone off for a given pid and update speakerphone state.
-     * @param pid
-     */
     @GuardedBy("mDeviceStateLock")
-    private void setSpeakerphoneOffForPid(int pid) {
-        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
+    /*package*/ void setCommunicationRouteForClient(
+                            IBinder cb, int pid, AudioDeviceAttributes device,
+                            int scoAudioMode, String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+        }
+
+        final boolean wasBtScoRequested = isBluetoothScoRequested();
+        final boolean wasSpeakerphoneRequested = isSpeakerphoneRequested();
+        CommunicationRouteClient client;
+
+
+        // Save previous client route in case of failure to start BT SCO audio
+        AudioDeviceAttributes prevClientDevice = null;
+        client = getCommunicationRouteClientForPid(pid);
+        if (client != null) {
+            prevClientDevice = client.getDevice();
+        }
+
+        if (device != null) {
+            client = addCommunicationRouteClient(cb, pid, device);
+            if (client == null) {
+                Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+                        + pid + " and device: " + device);
+            }
+        } else {
+            client = removeCommunicationRouteClient(cb, true);
+        }
         if (client == null) {
             return;
         }
-        client.unregisterDeathRecipient();
-        mSpeakerphoneClients.remove(client);
-        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
-                .append(pid).append(")").toString();
-        updateSpeakerphoneOn(eventSource);
-    }
 
-    @GuardedBy("mDeviceStateLock")
-    private void updateSpeakerphoneOn(String eventSource) {
-        if (isSpeakerphoneOnRequested()) {
-            if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+        boolean isBtScoRequested = isBluetoothScoRequested();
+        if (isBtScoRequested && !wasBtScoRequested) {
+            if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+                Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+                        + pid);
+                // clean up or restore previous client selection
+                if (prevClientDevice != null) {
+                    addCommunicationRouteClient(cb, pid, prevClientDevice);
+                } else {
+                    removeCommunicationRouteClient(cb, true);
+                }
             }
-            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
-        } else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
-            if (mBtHelper.isBluetoothScoOn()) {
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-                setForceUse_Async(
-                        AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
-            } else {
-                mForcedUseForComm = AudioSystem.FORCE_NONE;
+        } else if (!isBtScoRequested && wasBtScoRequested) {
+            mBtHelper.stopBluetoothSco(eventSource);
+        }
+
+        if (wasSpeakerphoneRequested != isSpeakerphoneRequested()) {
+            try {
+                mContext.sendBroadcastAsUser(
+                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
+                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+            } catch (Exception e) {
+                Log.w(TAG, "failed to broadcast ACTION_SPEAKERPHONE_STATE_CHANGED: " + e);
             }
         }
-        mForcedUseForCommExt = mForcedUseForComm;
-        setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
+
+        sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
     }
 
     /**
-     * Returns if speakerphone is requested ON or OFF.
-     * If the current audio mode owner is in the speakerphone client list, use this preference.
+     * Returns the device currently requested for communication use case.
+     * If the current audio mode owner is in the communication route client list,
+     * use this preference.
      * Otherwise use first client's preference (first client corresponds to latest request).
-     * Speakerphone is requested OFF if no client is in the list.
-     * @return true if speakerphone is requested ON, false otherwise
+     * null is returned if no client is in the list.
+     * @return AudioDeviceAttributes the requested device for communication.
      */
+
     @GuardedBy("mDeviceStateLock")
-    private boolean isSpeakerphoneOnRequested() {
-        if (mSpeakerphoneClients.isEmpty()) {
-            return false;
-        }
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private AudioDeviceAttributes requestedCommunicationDevice() {
+        AudioDeviceAttributes device = null;
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == mModeOwnerPid) {
-                return cl.isOn();
+                device = cl.getDevice();
             }
         }
-        return mSpeakerphoneClients.get(0).isOn();
+        if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
+            device = mCommunicationRouteClients.get(0).getDevice();
+        }
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "requestedCommunicationDevice, device: "
+                    + device + " mode owner pid: " + mModeOwnerPid);
+        }
+        return device;
     }
 
-    /*package*/ boolean isSpeakerphoneOn() {
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * speakerphone ON is currently requested or not.
+     * @return true if speakerphone ON requested, false otherwise.
+     */
+
+    private boolean isSpeakerphoneRequested() {
         synchronized (mDeviceStateLock) {
-            return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is speakerphone.
+     * @return true if speakerphone is active, false otherwise.
+     */
+    /*package*/ boolean isSpeakerphoneOn() {
+        return getForcedUseForComm() == AudioSystem.FORCE_SPEAKER;
+    }
+
+    /**
+     * Helper method on top of requestedCommunicationDevice() indicating if
+     * Bluetooth SCO ON is currently requested or not.
+     * @return true if Bluetooth SCO ON is requested, false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoRequested() {
+        synchronized (mDeviceStateLock) {
+            AudioDeviceAttributes device = requestedCommunicationDevice();
+            return device != null
+                    && device.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
         }
     }
 
@@ -348,7 +408,6 @@
         }
     }
 
-
     /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
             @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
             int profile, boolean suppressNoisyIntent, int a2dpVolume) {
@@ -431,42 +490,28 @@
         sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
     }
 
-    // never called by system components
-    /*package*/ void setBluetoothScoOnByApp(boolean on) {
-        synchronized (mDeviceStateLock) {
-            mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
-        }
-    }
 
-    /*package*/ boolean isBluetoothScoOnForApp() {
-        synchronized (mDeviceStateLock) {
-            return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
-        }
-    }
+    /**
+     * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+     */
+    private boolean mBluetoothScoOn;
 
     /*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
-        //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
-        synchronized (mDeviceStateLock) {
-            if (on) {
-                // do not accept SCO ON if SCO audio is not connected
-                if (!mBtHelper.isBluetoothScoOn()) {
-                    mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
-                    return;
-                }
-                mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
-            } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
-                mForcedUseForComm = isSpeakerphoneOnRequested()
-                        ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
-            }
-            mForcedUseForCommExt = mForcedUseForComm;
-            AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
-            sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
-                    AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "setBluetoothScoOn: " + on + " " + eventSource);
         }
-        // Un-mute ringtone stream volume
-        mAudioService.postUpdateRingerModeServiceInt();
+        synchronized (mDeviceStateLock) {
+            mBluetoothScoOn = on;
+            sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+        }
+    }
+
+    /**
+     * Indicates if active route selection for communication is Bluetooth SCO.
+     * @return true if Bluetooth SCO is active , false otherwise.
+     */
+    /*package*/ boolean isBluetoothScoOn() {
+        return getForcedUseForComm() == AudioSystem.FORCE_BT_SCO;
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
@@ -509,22 +554,43 @@
         sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+    /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
                 @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
-            // are mutually exclusive.
-            // See symmetrical operation for setSpeakerphoneOn(true).
-            setSpeakerphoneOffForPid(Binder.getCallingPid());
-            mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "startBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                AudioDeviceAttributes device = new AudioDeviceAttributes(
+                        AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "");
+                setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+                if (!isBluetoothScoRequested()) {
+                    Log.w(TAG, "startBluetoothScoForClient_Sync: rejected for pid: "
+                            + pid + " mode owner pid: " + mModeOwnerPid);
+                    postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                }
+            }
         }
     }
 
-    @GuardedBy("mSetModeLock")
-    /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
-        synchronized (mDeviceStateLock) {
-            mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+    /*package*/ void stopBluetoothScoForClient(
+                        IBinder cb, int pid, @NonNull String eventSource) {
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "stopBluetoothScoForClient_Sync, pid: " + pid);
+        }
+
+        synchronized (mSetModeLock) {
+            synchronized (mDeviceStateLock) {
+                CommunicationRouteClient client = getCommunicationRouteClientForPid(pid);
+                if (client == null || !client.requestsBluetoothSco()) {
+                    return;
+                }
+                setCommunicationRouteForClient(
+                        cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+            }
         }
     }
 
@@ -696,12 +762,8 @@
                 hearingAidProfile);
     }
 
-    /*package*/ void postScoClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
-    }
-
-    /*package*/ void postSpeakerphoneClientDied(Object obj) {
-        sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
+    /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
+        sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
 
     /*package*/ void postSaveSetPreferredDevicesForStrategy(int strategy,
@@ -821,15 +883,14 @@
 
         mDeviceInventory.dump(pw, prefix);
 
+        pw.println("\n" + prefix + "Communication route clients:");
+        mCommunicationRouteClients.forEach((cl) -> {
+            pw.println("  " + prefix + "pid: " + cl.getPid() + " device: "
+                        + cl.getDevice() + " cb: " + cl.getBinder()); });
+
         pw.println("\n" + prefix + "mForcedUseForComm: "
                 +  AudioSystem.forceUseConfigToString(mForcedUseForComm));
-        pw.println(prefix + "mForcedUseForCommExt: "
-                + AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
         pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
-        pw.println(prefix + "Speakerphone clients:");
-        mSpeakerphoneClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " on: "
-                        + cl.isOn() + " cb: " + cl.getBinder()); });
 
         mBtHelper.dump(pw, prefix);
     }
@@ -852,6 +913,11 @@
                 .set(MediaMetrics.Property.FORCE_USE_MODE,
                         AudioSystem.forceUseConfigToString(config))
                 .record();
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onSetForceUse(useCase<" + useCase + ">, config<" + config + ">, fromA2dp<"
+                    + fromA2dp + ">, eventSource<" + eventSource + ">)");
+        }
         AudioSystem.setForceUse(useCase, config);
     }
 
@@ -917,9 +983,12 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_RESTORE_DEVICES:
-                    synchronized (mDeviceStateLock) {
-                        mDeviceInventory.onRestoreDevices();
-                        mBtHelper.onAudioServerDiedRestoreA2dp();
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            mDeviceInventory.onRestoreDevices();
+                            mBtHelper.onAudioServerDiedRestoreA2dp();
+                            onUpdateCommunicationRoute("MSG_RESTORE_DEVICES");
+                        }
                     }
                     break;
                 case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1009,25 +1078,24 @@
                             if (mModeOwnerPid != msg.arg1) {
                                 mModeOwnerPid = msg.arg1;
                                 if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
-                                    updateSpeakerphoneOn("setNewModeOwner");
-                                }
-                                if (mModeOwnerPid != 0) {
-                                    mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
+                                    onUpdateCommunicationRoute("setNewModeOwner");
                                 }
                             }
                         }
                     }
                     break;
-                case MSG_L_SCOCLIENT_DIED:
+                case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
-                            mBtHelper.scoClientDied(msg.obj);
+                            onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
                         }
                     }
                     break;
-                case MSG_L_SPEAKERPHONE_CLIENT_DIED:
-                    synchronized (mDeviceStateLock) {
-                        speakerphoneClientDied(msg.obj);
+                case MSG_L_UPDATE_COMMUNICATION_ROUTE:
+                    synchronized (mSetModeLock) {
+                        synchronized (mDeviceStateLock) {
+                            onUpdateCommunicationRoute((String) msg.obj);
+                        }
                     }
                     break;
                 case MSG_TOGGLE_HDMI:
@@ -1207,17 +1275,17 @@
     // process external command to (dis)connect a hearing aid device
     private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31;
 
-    // a ScoClient died in BtHelper
-    private static final int MSG_L_SCOCLIENT_DIED = 32;
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 33;
-    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 34;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY = 32;
+    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICES_FOR_STRATEGY = 33;
 
-    private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
-    private static final int MSG_CHECK_MUTE_MUSIC = 36;
-    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 37;
+    private static final int MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED = 34;
+    private static final int MSG_CHECK_MUTE_MUSIC = 35;
+    private static final int MSG_REPORT_NEW_ROUTES_A2DP = 36;
 
-    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
-    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 39;
+    private static final int MSG_IL_SAVE_PREF_DEVICES_FOR_CAPTURE_PRESET = 37;
+    private static final int MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET = 38;
+
+    private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
 
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -1372,14 +1440,20 @@
         }
     }
 
-    private class SpeakerphoneClient implements IBinder.DeathRecipient {
+    // List of applications requesting a specific route for communication.
+    @GuardedBy("mDeviceStateLock")
+    private final @NonNull LinkedList<CommunicationRouteClient> mCommunicationRouteClients =
+            new LinkedList<CommunicationRouteClient>();
+
+    private class CommunicationRouteClient implements IBinder.DeathRecipient {
         private final IBinder mCb;
         private final int mPid;
-        private final boolean mOn;
-        SpeakerphoneClient(IBinder cb, int pid, boolean on) {
+        private AudioDeviceAttributes mDevice;
+
+        CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
             mCb = cb;
             mPid = pid;
-            mOn = on;
+            mDevice = device;
         }
 
         public boolean registerDeathRecipient() {
@@ -1388,7 +1462,7 @@
                 mCb.linkToDeath(this, 0);
                 status = true;
             } catch (RemoteException e) {
-                Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
+                Log.w(TAG, "CommunicationRouteClient could not link to " + mCb + " binder death");
             }
             return status;
         }
@@ -1397,13 +1471,13 @@
             try {
                 mCb.unlinkToDeath(this, 0);
             } catch (NoSuchElementException e) {
-                Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
+                Log.w(TAG, "CommunicationRouteClient could not not unregistered to binder");
             }
         }
 
         @Override
         public void binderDied() {
-            postSpeakerphoneClientDied(this);
+            postCommunicationRouteClientDied(this);
         }
 
         IBinder getBinder() {
@@ -1414,29 +1488,99 @@
             return mPid;
         }
 
-        boolean isOn() {
-            return mOn;
+        AudioDeviceAttributes getDevice() {
+            return mDevice;
+        }
+
+        boolean requestsBluetoothSco() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BLUETOOTH_SCO;
+        }
+
+        boolean requestsSpeakerphone() {
+            return mDevice != null
+                    && mDevice.getType()
+                        == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER;
         }
     }
 
+    // @GuardedBy("mSetModeLock")
     @GuardedBy("mDeviceStateLock")
-    private void speakerphoneClientDied(Object obj) {
-        if (obj == null) {
+    private void onCommunicationRouteClientDied(CommunicationRouteClient client) {
+        if (client == null) {
             return;
         }
         Log.w(TAG, "Speaker client died");
-        if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
-            updateSpeakerphoneOn("speakerphoneClientDied");
+        if (removeCommunicationRouteClient(client.getBinder(), false)
+                != null) {
+            onUpdateCommunicationRoute("onCommunicationRouteClientDied");
         }
     }
 
-    private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    /**
+     * Determines which forced usage for communication should be sent to audio policy manager
+     * as a function of current SCO audio activation state and active communication route requests.
+     * SCO audio state has the highest priority as it can result from external activation by
+     * telephony service.
+     * @return selected forced usage for communication.
+     */
+    @GuardedBy("mDeviceStateLock")
+    private int getForcedUseForComm() {
+        boolean btSCoOn = mBluetoothScoOn && mBtHelper.isBluetoothScoOn();
+
+        if (btSCoOn) {
+            return AudioSystem.FORCE_BT_SCO;
+        }
+        if (isSpeakerphoneRequested()) {
+            return AudioSystem.FORCE_SPEAKER;
+        }
+        return AudioSystem.FORCE_NONE;
+    }
+
+    /**
+     * Configures audio policy manager and audio HAL according to active communication route.
+     * Always called from message Handler.
+     */
+    // @GuardedBy("mSetModeLock")
+    @GuardedBy("mDeviceStateLock")
+    private void onUpdateCommunicationRoute(String eventSource) {
+        mForcedUseForComm = getForcedUseForComm();
+
+        if (AudioService.DEBUG_COMM_RTE) {
+            Log.v(TAG, "onUpdateCommunicationRoute, mForcedUseForComm: " + mForcedUseForComm
+                    + " eventSource: " + eventSource);
+        }
+
+        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
+            AudioSystem.setParameters("BT_SCO=on");
+            setForceUse_Async(
+                    AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO, eventSource);
+            setForceUse_Async(
+                    AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
+        } else {
+            AudioSystem.setParameters("BT_SCO=off");
+            setForceUse_Async(
+                    AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
+            if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
+                setForceUse_Async(
+                        AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER, eventSource);
+            } else {
+                setForceUse_Async(
+                        AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE, eventSource);
+            }
+        }
+        mAudioService.postUpdateRingerModeServiceInt();
+    }
+
+    private CommunicationRouteClient removeCommunicationRouteClient(
+                    IBinder cb, boolean unregister) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getBinder() == cb) {
                 if (unregister) {
                     cl.unregisterDeathRecipient();
                 }
-                mSpeakerphoneClients.remove(cl);
+                mCommunicationRouteClients.remove(cl);
                 return cl;
             }
         }
@@ -1444,30 +1588,25 @@
     }
 
     @GuardedBy("mDeviceStateLock")
-    private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
+    private CommunicationRouteClient addCommunicationRouteClient(
+                    IBinder cb, int pid, AudioDeviceAttributes device) {
         // always insert new request at first position
-        removeSpeakerphoneClient(cb, true);
-        SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
+        removeCommunicationRouteClient(cb, true);
+        CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
         if (client.registerDeathRecipient()) {
-            mSpeakerphoneClients.add(0, client);
-            return true;
+            mCommunicationRouteClients.add(0, client);
+            return client;
         }
-        return false;
+        return null;
     }
 
     @GuardedBy("mDeviceStateLock")
-    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
-        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
+    private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+        for (CommunicationRouteClient cl : mCommunicationRouteClients) {
             if (cl.getPid() == pid) {
                 return cl;
             }
         }
         return null;
     }
-
-    // List of clients requesting speakerPhone ON
-    @GuardedBy("mDeviceStateLock")
-    private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
-            new ArrayList<SpeakerphoneClient>();
-
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ff6367b..662d8d5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -207,6 +207,9 @@
     /** debug calls to devices APIs */
     protected static final boolean DEBUG_DEVICES = false;
 
+    /** Debug communication route */
+    protected static final boolean DEBUG_COMM_RTE = false;
+
     /** How long to delay before persisting a change in volume/ringer mode. */
     private static final int PERSIST_DELAY = 500;
 
@@ -3765,7 +3768,7 @@
         final boolean ringerModeMute = ringerMode == AudioManager.RINGER_MODE_VIBRATE
                 || ringerMode == AudioManager.RINGER_MODE_SILENT;
         final boolean shouldRingSco = ringerMode == AudioManager.RINGER_MODE_VIBRATE
-                && isBluetoothScoOn();
+                && mDeviceBroker.isBluetoothScoOn();
         // Ask audio policy engine to force use Bluetooth SCO channel if needed
         final String eventSource = "muteRingerModeStreams() from u/pid:" + Binder.getCallingUid()
                 + "/" + Binder.getCallingPid();
@@ -4406,10 +4409,10 @@
         // for logging only
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
+
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
                 + MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
                 .setUid(uid)
@@ -4417,17 +4420,9 @@
                 .set(MediaMetrics.Property.STATE, on
                         ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
                 .record();
-
-        if (stateChanged) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                mContext.sendBroadcastAsUser(
-                        new Intent(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)
-                                .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+        Binder.restoreCallingIdentity(ident);
     }
 
     /** @see AudioManager#isSpeakerphoneOn() */
@@ -4435,6 +4430,11 @@
         return mDeviceBroker.isSpeakerphoneOn();
     }
 
+
+    /** BT SCO audio state seen by apps using the deprecated API setBluetoothScoOn().
+     * @see isBluetoothScoOn() */
+    private boolean mBtScoOnByApp;
+
     /** @see AudioManager#setBluetoothScoOn(boolean) */
     public void setBluetoothScoOn(boolean on) {
         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
@@ -4443,7 +4443,7 @@
 
         // Only enable calls from system components
         if (UserHandle.getCallingAppId() >= FIRST_APPLICATION_UID) {
-            mDeviceBroker.setBluetoothScoOnByApp(on);
+            mBtScoOnByApp = on;
             return;
         }
 
@@ -4469,7 +4469,7 @@
      * Note that it doesn't report internal state, but state seen by apps (which may have
      * called setBluetoothScoOn() */
     public boolean isBluetoothScoOn() {
-        return mDeviceBroker.isBluetoothScoOnForApp();
+        return mBtScoOnByApp || mDeviceBroker.isBluetoothScoOn();
     }
 
     // TODO investigate internal users due to deprecation of SDK API
@@ -4516,7 +4516,7 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(scoAudioMode))
                 .record();
-        startBluetoothScoInt(cb, scoAudioMode, eventSource);
+        startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
 
     }
 
@@ -4535,10 +4535,10 @@
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
                         BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
                 .record();
-        startBluetoothScoInt(cb, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+        startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
     }
 
-    void startBluetoothScoInt(IBinder cb, int scoAudioMode, @NonNull String eventSource) {
+    void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
         MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
                 .set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -4549,9 +4549,9 @@
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
             return;
         }
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+        Binder.restoreCallingIdentity(ident);
         mmi.record();
     }
 
@@ -4566,9 +4566,9 @@
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        synchronized (mDeviceBroker.mSetModeLock) {
-            mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
-        }
+        final long ident = Binder.clearCallingIdentity();
+        mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+        Binder.restoreCallingIdentity(ident);
         new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
                 .setUid(uid)
                 .setPid(pid)
@@ -7848,6 +7848,7 @@
         pw.print("  mHasVibrator="); pw.println(mHasVibrator);
         pw.print("  mVolumePolicy="); pw.println(mVolumePolicy);
         pw.print("  mAvrcpAbsVolSupported="); pw.println(mAvrcpAbsVolSupported);
+        pw.print("  mBtScoOnByApp="); pw.println(mBtScoOnByApp);
         pw.print("  mIsSingleVolume="); pw.println(mIsSingleVolume);
         pw.print("  mUseFixedVolume="); pw.println(mUseFixedVolume);
         pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 8de31d9..ae0e805 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -30,8 +30,6 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -39,9 +37,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.List;
-import java.util.NoSuchElementException;
 import java.util.Objects;
 
 /**
@@ -58,10 +54,6 @@
         mDeviceBroker = broker;
     }
 
-    // List of clients having issued a SCO start request
-    @GuardedBy("BtHelper.this")
-    private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
-
     // BluetoothHeadset API to control SCO connection
     private @Nullable BluetoothHeadset mBluetoothHeadset;
 
@@ -301,6 +293,8 @@
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void receiveBtEvent(Intent intent) {
         final String action = intent.getAction();
+
+        Log.i(TAG, "receiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
         if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
             BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
             setBtScoActiveDevice(btDevice);
@@ -308,20 +302,16 @@
             boolean broadcast = false;
             int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
             int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            // broadcast intent if the connection was initated by AudioService
-            if (!mScoClients.isEmpty()
-                    && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
-                    || mScoAudioState == SCO_STATE_ACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
-                    || mScoAudioState == SCO_STATE_DEACTIVATING)) {
-                broadcast = true;
-            }
+            Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
             switch (btState) {
                 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                     scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                     if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+                    } else if (mDeviceBroker.isBluetoothScoRequested()) {
+                        // broadcast intent if the connection was initated by AudioService
+                        broadcast = true;
                     }
                     mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
                     break;
@@ -333,21 +323,21 @@
                     // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
                     // 2) If audio was connected then disconnected via Bluetooth APIs and
                     // we still have pending activation requests by apps: this is indicated by
-                    // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
+                    // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
                             || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
-                                    && !mScoClients.isEmpty())) {
+                                    && mDeviceBroker.isBluetoothScoRequested())) {
                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
                                 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
                                 mBluetoothHeadsetDevice, mScoAudioMode)) {
                             mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                            broadcast = false;
+                            scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+                            broadcast = true;
                             break;
                         }
                     }
-                    // Tear down SCO if disconnected from external
-                    if (mScoAudioState == SCO_STATE_DEACTIVATING) {
-                        clearAllScoClients(0, false);
+                    if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+                        broadcast = true;
                     }
                     mScoAudioState = SCO_STATE_INACTIVE;
                     break;
@@ -356,11 +346,8 @@
                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                     }
-                    broadcast = false;
                     break;
                 default:
-                    // do not broadcast CONNECTING or invalid state
-                    broadcast = false;
                     break;
             }
             if (broadcast) {
@@ -386,81 +373,19 @@
                 == BluetoothHeadset.STATE_AUDIO_CONNECTED;
     }
 
-    /**
-     * Disconnect all SCO connections started by {@link AudioManager} except those started by
-     * {@param exceptPid}
-     *
-     * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
-     */
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
-        checkScoAudioState();
-        if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
-            return;
-        }
-        clearAllScoClients(exceptPid, true);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
+    /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
                 @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, true);
-        // The calling identity must be cleared before calling ScoClient.incCount().
-        // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        // The caller identity must be cleared after getScoClient() because it is needed if a new
-        // client is created.
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-            client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
-        } catch (NullPointerException e) {
-            Log.e(TAG, "Null ScoClient", e);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
-            @NonNull String eventSource) {
-        ScoClient client = getScoClient(cb, false);
-        // The calling identity must be cleared before calling ScoClient.decCount().
-        // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
-        // and this must be done on behalf of system server to make sure permissions are granted.
-        final long ident = Binder.clearCallingIdentity();
-        if (client != null) {
-            stopAndRemoveClient(client, eventSource);
-        }
-        Binder.restoreCallingIdentity(ident);
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
-        ScoClient client = getScoClientForPid(pid);
-        if (client == null) {
-            return;
-        }
-        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
-                .append(pid).append(")").toString();
-        stopAndRemoveClient(client, eventSource);
-    }
-
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    // @GuardedBy("BtHelper.this")
-    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
         AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
-        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                SCO_MODE_VIRTUAL_CALL);
-        // If a disconnection is pending, the client will be removed when clearAllScoClients()
-        // is called form receiveBtEvent()
-        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
-                && mScoAudioState != SCO_STATE_DEACTIVATING) {
-            client.remove(false /*stop */, true /*unregister*/);
-        }
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
+    }
+
+    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
+        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
+        return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
@@ -507,7 +432,6 @@
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
-        clearAllScoClients(0, false);
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
         AudioSystem.setParameters("A2dpSuspended=false");
@@ -740,196 +664,122 @@
             };
 
     //----------------------------------------------------------------------
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    /*package*/ synchronized void scoClientDied(Object obj) {
-        final ScoClient client = (ScoClient) obj;
-        client.remove(true /*stop*/, false /*unregister*/);
-        Log.w(TAG, "SCO client died");
-    }
-
-    private class ScoClient implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-        private int mCreatorPid;
-
-        ScoClient(IBinder cb) {
-            mCb = cb;
-            mCreatorPid = Binder.getCallingPid();
-        }
-
-        public void registerDeathRecipient() {
-            try {
-                mCb.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
-            }
-        }
-
-        public void unregisterDeathRecipient() {
-            try {
-                mCb.unlinkToDeath(this, 0);
-            } catch (NoSuchElementException e) {
-                Log.w(TAG, "ScoClient could not not unregistered to binder");
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            // process this from DeviceBroker's message queue to take the right locks since
-            // this event can impact SCO mode and requires querying audio mode stack
-            mDeviceBroker.postScoClientDied(this);
-        }
-
-        IBinder getBinder() {
-            return mCb;
-        }
-
-        int getPid() {
-            return mCreatorPid;
-        }
-
-        // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-        //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-        @GuardedBy("BtHelper.this")
-        private boolean requestScoState(int state, int scoAudioMode) {
-            checkScoAudioState();
-            if (mScoClients.size() != 1) {
-                Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
-                        + ", num SCO clients=" + mScoClients.size());
-                return true;
-            }
-            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
-                // Make sure that the state transitions to CONNECTING even if we cannot initiate
-                // the connection.
-                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
-                // Accept SCO audio activation only in NORMAL audio mode or if the mode is
-                // currently controlled by the same client process.
-                final int modeOwnerPid =  mDeviceBroker.getModeOwnerPid();
-                if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
-                    Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
-                            + modeOwnerPid + " != creatorPid " + mCreatorPid);
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    @GuardedBy("BtHelper.this")
+    private boolean requestScoState(int state, int scoAudioMode) {
+        checkScoAudioState();
+        if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            // Make sure that the state transitions to CONNECTING even if we cannot initiate
+            // the connection.
+            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+            switch (mScoAudioState) {
+                case SCO_STATE_INACTIVE:
+                    mScoAudioMode = scoAudioMode;
+                    if (scoAudioMode == SCO_MODE_UNDEFINED) {
+                        mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                        if (mBluetoothHeadsetDevice != null) {
+                            mScoAudioMode = Settings.Global.getInt(
+                                    mDeviceBroker.getContentResolver(),
+                                    "bluetooth_sco_channel_"
+                                            + mBluetoothHeadsetDevice.getAddress(),
+                                    SCO_MODE_VIRTUAL_CALL);
+                            if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
+                                mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
+                            }
+                        }
+                    }
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                        } else {
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " connection, mScoAudioMode=" + mScoAudioMode);
+                            broadcastScoConnectionState(
+                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                            return false;
+                        }
+                        break;
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
+                        Log.w(TAG, "requestScoState: no active device while connecting,"
+                                + " mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    } else {
+                        Log.w(TAG, "requestScoState: connect to "
+                                + getAnonymizedAddress(mBluetoothHeadsetDevice)
+                                + " failed, mScoAudioMode=" + mScoAudioMode);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        return false;
+                    }
+                    break;
+                case SCO_STATE_DEACTIVATING:
+                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                    break;
+                case SCO_STATE_DEACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+                    break;
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to connect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                     return false;
-                }
-                switch (mScoAudioState) {
-                    case SCO_STATE_INACTIVE:
-                        mScoAudioMode = scoAudioMode;
-                        if (scoAudioMode == SCO_MODE_UNDEFINED) {
-                            mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                            if (mBluetoothHeadsetDevice != null) {
-                                mScoAudioMode = Settings.Global.getInt(
-                                        mDeviceBroker.getContentResolver(),
-                                        "bluetooth_sco_channel_"
-                                                + mBluetoothHeadsetDevice.getAddress(),
-                                        SCO_MODE_VIRTUAL_CALL);
-                                if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
-                                    mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
-                                }
-                            }
-                        }
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " connection, mScoAudioMode=" + mScoAudioMode);
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            Log.w(TAG, "requestScoState: no active device while connecting,"
-                                    + " mScoAudioMode=" + mScoAudioMode);
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            return false;
-                        }
-                        if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+            }
+        } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+            switch (mScoAudioState) {
+                case SCO_STATE_ACTIVE_INTERNAL:
+                    if (mBluetoothHeadset == null) {
+                        if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
                         } else {
-                            Log.w(TAG, "requestScoState: connect to "
-                                    + getAnonymizedAddress(mBluetoothHeadsetDevice)
-                                    + " failed, mScoAudioMode=" + mScoAudioMode);
+                            Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+                                    + " disconnection, mScoAudioMode=" + mScoAudioMode);
+                            mScoAudioState = SCO_STATE_INACTIVE;
                             broadcastScoConnectionState(
                                     AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                             return false;
                         }
                         break;
-                    case SCO_STATE_DEACTIVATING:
-                        mScoAudioState = SCO_STATE_ACTIVATE_REQ;
-                        break;
-                    case SCO_STATE_DEACTIVATE_REQ:
-                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
-                        break;
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
-                        break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to connect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
-            } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
-                switch (mScoAudioState) {
-                    case SCO_STATE_ACTIVE_INTERNAL:
-                        if (mBluetoothHeadset == null) {
-                            if (getBluetoothHeadset()) {
-                                mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
-                            } else {
-                                Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
-                                        + " disconnection, mScoAudioMode=" + mScoAudioMode);
-                                mScoAudioState = SCO_STATE_INACTIVE;
-                                broadcastScoConnectionState(
-                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                                return false;
-                            }
-                            break;
-                        }
-                        if (mBluetoothHeadsetDevice == null) {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                            break;
-                        }
-                        if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
-                                mBluetoothHeadsetDevice, mScoAudioMode)) {
-                            mScoAudioState = SCO_STATE_DEACTIVATING;
-                        } else {
-                            mScoAudioState = SCO_STATE_INACTIVE;
-                            broadcastScoConnectionState(
-                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        }
-                        break;
-                    case SCO_STATE_ACTIVATE_REQ:
+                    }
+                    if (mBluetoothHeadsetDevice == null) {
                         mScoAudioState = SCO_STATE_INACTIVE;
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                         break;
-                    default:
-                        Log.w(TAG, "requestScoState: failed to disconnect in state "
-                                + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
-                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
-                        return false;
-                }
+                    }
+                    if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
+                            mBluetoothHeadsetDevice, mScoAudioMode)) {
+                        mScoAudioState = SCO_STATE_DEACTIVATING;
+                    } else {
+                        mScoAudioState = SCO_STATE_INACTIVE;
+                        broadcastScoConnectionState(
+                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    }
+                    break;
+                case SCO_STATE_ACTIVATE_REQ:
+                    mScoAudioState = SCO_STATE_INACTIVE;
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    break;
+                default:
+                    Log.w(TAG, "requestScoState: failed to disconnect in state "
+                            + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    return false;
             }
-            return true;
         }
-
-        @GuardedBy("BtHelper.this")
-        void remove(boolean stop, boolean unregister) {
-            if (unregister) {
-                unregisterDeathRecipient();
-            }
-            if (stop) {
-                requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
-                        SCO_MODE_VIRTUAL_CALL);
-            }
-            mScoClients.remove(this);
-        }
+        return true;
     }
 
     //-----------------------------------------------------
@@ -982,49 +832,6 @@
         }
     }
 
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClient(IBinder cb, boolean create) {
-        for (ScoClient existingClient : mScoClients) {
-            if (existingClient.getBinder() == cb) {
-                return existingClient;
-            }
-        }
-        if (create) {
-            ScoClient newClient = new ScoClient(cb);
-            newClient.registerDeathRecipient();
-            mScoClients.add(newClient);
-            return newClient;
-        }
-        return null;
-    }
-
-    @GuardedBy("BtHelper.this")
-    private ScoClient getScoClientForPid(int pid) {
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() == pid) {
-                return cl;
-            }
-        }
-        return null;
-    }
-
-    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
-    @GuardedBy("BtHelper.this")
-    private void clearAllScoClients(int exceptPid, boolean stopSco) {
-        final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
-        for (ScoClient cl : mScoClients) {
-            if (cl.getPid() != exceptPid) {
-                clients.add(cl);
-            }
-        }
-        for (ScoClient cl : clients) {
-            cl.remove(stopSco, true /*unregister*/);
-        }
-
-    }
-
     private boolean getBluetoothHeadset() {
         boolean result = false;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1070,10 +877,6 @@
         pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
         pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
         pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
-        pw.println(prefix + "Sco clients:");
-        mScoClients.forEach((cl) -> {
-            pw.println("  " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
-
         pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
         pw.println(prefix + "mA2dp: " + mA2dp);
         pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
diff --git a/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.java
new file mode 100644
index 0000000..eff4da3
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/HardwareAuthTokenUtils.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.server.biometrics;
+
+import static java.nio.ByteOrder.LITTLE_ENDIAN;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+
+import java.nio.ByteOrder;
+
+/**
+ * Utilities for converting between old and new HardwareAuthToken types. See
+ * {@link HardwareAuthToken}.
+ */
+public class HardwareAuthTokenUtils {
+    public static byte[] toByteArray(HardwareAuthToken hat) {
+        final byte[] array = new byte[69];
+
+        // Version, first byte. Used in hw_auth_token.h but not HardwareAuthToken
+        array[0] = 0;
+
+        // Challenge, 1:8.
+        writeLong(hat.challenge, array, 1 /* offset */);
+
+        // UserId, 9:16.
+        writeLong(hat.userId, array, 9 /* offset */);
+
+        // AuthenticatorId, 17:24.
+        writeLong(hat.authenticatorId, array, 17 /* offset */);
+
+        // AuthenticatorType, 25:28.
+        writeInt(flipIfNativelyLittle(hat.authenticatorType), array, 25 /* offset */);
+
+        // Timestamp, 29:36.
+        writeLong(flipIfNativelyLittle(hat.timestamp.milliSeconds), array, 29 /* offset */);
+
+        // MAC, 37:69. Byte array.
+        System.arraycopy(hat.mac, 0 /* srcPos */, array, 37 /* destPos */, hat.mac.length);
+
+        return array;
+    }
+
+    public static HardwareAuthToken toHardwareAuthToken(byte[] array) {
+        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+
+        // First byte is version, which doesn't not exist in HardwareAuthToken anymore
+        // Next 8 bytes is the challenge.
+        hardwareAuthToken.challenge = getLong(array, 1 /* offset */);
+
+        // Next 8 bytes is the userId
+        hardwareAuthToken.userId = getLong(array, 9 /* offset */);
+
+        // Next 8 bytes is the authenticatorId.
+        hardwareAuthToken.authenticatorId = getLong(array, 17 /* offset */);
+
+        // Next 4 bytes is the authenticatorType.
+        hardwareAuthToken.authenticatorType = flipIfNativelyLittle(getInt(array, 25 /* offset */));
+
+        // Next 8 bytes is the timestamp.
+        final Timestamp timestamp = new Timestamp();
+        timestamp.milliSeconds = flipIfNativelyLittle(getLong(array, 29 /* offset */));
+        hardwareAuthToken.timestamp = timestamp;
+
+        // Last 32 bytes is the mac, 37:69
+        hardwareAuthToken.mac = new byte[32];
+        System.arraycopy(array, 37 /* srcPos */,
+                hardwareAuthToken.mac,
+                0 /* destPos */,
+                32 /* length */);
+
+        return hardwareAuthToken;
+    }
+
+    private static long flipIfNativelyLittle(long l) {
+        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+            return Long.reverseBytes(l);
+        }
+        return l;
+    }
+
+    private static int flipIfNativelyLittle(int i) {
+        if (LITTLE_ENDIAN == ByteOrder.nativeOrder()) {
+            return Integer.reverseBytes(i);
+        }
+        return i;
+    }
+
+    private static void writeLong(long l, byte[] dest, int offset) {
+        dest[offset + 0] = (byte) l;
+        dest[offset + 1] = (byte) (l >> 8);
+        dest[offset + 2] = (byte) (l >> 16);
+        dest[offset + 3] = (byte) (l >> 24);
+        dest[offset + 4] = (byte) (l >> 32);
+        dest[offset + 5] = (byte) (l >> 40);
+        dest[offset + 6] = (byte) (l >> 48);
+        dest[offset + 7] = (byte) (l >> 56);
+    }
+
+    private static void writeInt(int i, byte[] dest, int offset) {
+        dest[offset + 0] = (byte) i;
+        dest[offset + 1] = (byte) (i >> 8);
+        dest[offset + 2] = (byte) (i >> 16);
+        dest[offset + 3] = (byte) (i >> 24);
+    }
+
+    private static long getLong(byte[] array, int offset) {
+        long result = 0;
+        // Lowest bit is LSB
+        for (int i = 0; i < 8; i++) {
+            result += (long) ((array[i + offset] & 0xffL) << (8 * i));
+        }
+        return result;
+    }
+
+    private static int getInt(byte[] array, int offset) {
+        int result = 0;
+        // Lowest bit is LSB
+        for (int i = 0; i < 4; i++) {
+            result += (int) (((int) array[i + offset] & 0xff) << (8 * i));
+        }
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 3c9dddd..0dee816 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -73,7 +73,7 @@
         T getDaemon();
     }
 
-    private final int mSequentialId;
+    protected final int mSequentialId;
     @NonNull private final Context mContext;
     @NonNull protected final LazyDaemon<T> mLazyDaemon;
     private final int mTargetUserId;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d353994..45de538 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.MANAGE_BIOMETRIC;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.TEST_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
@@ -32,6 +33,10 @@
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.IBiometricSensorReceiver;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.biometrics.ITestService;
+import android.hardware.biometrics.SensorPropertiesInternal;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -40,9 +45,12 @@
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.NativeHandle;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.EventLog;
@@ -53,11 +61,13 @@
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
 import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21;
 import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock;
 
@@ -81,11 +91,74 @@
     private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
     private final LockPatternUtils mLockPatternUtils;
     @NonNull private List<ServiceProvider> mServiceProviders;
+    @Nullable private TestService mTestService;
+
+    private final class TestService extends ITestService.Stub {
+
+        @Override
+        public List<SensorPropertiesInternal> getSensorPropertiesInternal(
+                String opPackageName) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+            return null;
+        }
+
+        @Override
+        public void enableTestHal(int sensorId, boolean enableTestHal) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void enrollStart(int sensorId, int userId) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void enrollFinish(int sensorId, int userId) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void authenticateSuccess(int sensorId, int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void authenticateReject(int sensorId, int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void notifyAcquired(int sensorId, int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void notifyError(int sensorId, int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+
+        @Override
+        public void internalCleanup(int sensorId, int userId)  {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+        }
+    }
 
     /**
      * Receives the incoming binder calls from FingerprintManager.
      */
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        @Override
+        public ITestService getTestService(String opPackageName) {
+            Utils.checkPermission(getContext(), TEST_BIOMETRIC);
+
+            synchronized (this) {
+                if (mTestService == null) {
+                    mTestService = new TestService();
+                }
+            }
+            return mTestService;
+        }
+
         @Override // Binder call
         public List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(
                 String opPackageName) {
@@ -114,7 +187,7 @@
         }
 
         @Override // Binder call
-        public void revokeChallenge(IBinder token, String opPackageName) {
+        public void revokeChallenge(IBinder token, String opPackageName, long challenge) {
             Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
 
             final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -123,7 +196,8 @@
                 return;
             }
 
-            provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
+            provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName,
+                    challenge);
         }
 
         @Override // Binder call
@@ -506,7 +580,7 @@
         }
 
         @Override
-        public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+        public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -514,11 +588,11 @@
                 Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
                 return;
             }
-            provider.onFingerDown(sensorId, x, y, minor, major);
+            provider.onPointerDown(sensorId, x, y, minor, major);
         }
 
         @Override
-        public void onFingerUp(int sensorId) {
+        public void onPointerUp(int sensorId) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
             final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -526,7 +600,7 @@
                 Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
                 return;
             }
-            provider.onFingerUp(sensorId);
+            provider.onPointerUp(sensorId);
         }
 
         @Override
@@ -546,6 +620,39 @@
         mLockoutResetDispatcher = new LockoutResetDispatcher(context);
         mLockPatternUtils = new LockPatternUtils(context);
         mServiceProviders = new ArrayList<>();
+
+        initializeAidlHals();
+    }
+
+    private void initializeAidlHals() {
+        final String[] instances = ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR);
+        if (instances == null || instances.length == 0) {
+            return;
+        }
+
+        // If for some reason the HAL is not started before the system service, do not block
+        // the rest of system server. Put this on a background thread.
+        final ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+                true /* allowIo */);
+        thread.start();
+        final Handler handler = new Handler(thread.getLooper());
+
+        handler.post(() -> {
+            for (String instance : instances) {
+                final String fqName = IFingerprint.DESCRIPTOR + "/" + instance;
+                final IFingerprint fp = IFingerprint.Stub.asInterface(
+                        ServiceManager.waitForDeclaredService(fqName));
+                try {
+                    final SensorProps[] props = fp.getSensorProps();
+                    final FingerprintProvider provider =
+                            new FingerprintProvider(getContext(), props, fqName,
+                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+                    mServiceProviders.add(provider);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Remote exception when initializing instance: " + fqName);
+                }
+            }
+        });
     }
 
     @Override
@@ -571,6 +678,7 @@
     private Pair<Integer, ServiceProvider> getSingleProvider() {
         final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
         if (properties.size() != 1) {
+            Slog.e(TAG, "Multiple sensors found: " + properties.size());
             return null;
         }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index d7338a0..35d0188 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -68,7 +68,7 @@
             @NonNull IFingerprintServiceReceiver receiver, String opPackageName);
 
     void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
-            @NonNull String opPackageName);
+            @NonNull String opPackageName, long challenge);
 
     void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@@ -92,6 +92,8 @@
             @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
             @NonNull String opPackageName);
 
+    void scheduleInternalCleanup(int sensorId, int userId);
+
     boolean isHardwareDetected(int sensorId);
 
     void rename(int sensorId, int fingerId, int userId, @NonNull String name);
@@ -102,9 +104,9 @@
 
     long getAuthenticatorId(int sensorId, int userId);
 
-    void onFingerDown(int sensorId, int x, int y, float minor, float major);
+    void onPointerDown(int sensorId, int x, int y, float minor, float major);
 
-    void onFingerUp(int sensorId);
+    void onPointerUp(int sensorId);
 
     void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
similarity index 85%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index 74cae02..0bf107a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package com.android.server.biometrics.sensors.fingerprint;
 
 /**
  * Interface for under-display fingerprint sensors.
@@ -22,6 +22,6 @@
  * finger position (e.g. enroll, authenticate) should implement this.
  */
 public interface Udfps {
-    void onFingerDown(int x, int y, float minor, float major);
-    void onFingerUp();
+    void onPointerDown(int x, int y, float minor, float major);
+    void onPointerUp();
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
similarity index 89%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index 0f1d6b4..a2b871e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.biometrics.sensors.fingerprint.hidl;
+package com.android.server.biometrics.sensors.fingerprint;
 
 import android.annotation.Nullable;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -29,7 +29,7 @@
 
     private static final String TAG = "UdfpsHelper";
 
-    static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
+    public static void onFingerDown(IBiometricsFingerprint daemon, int x, int y, float minor,
             float major) {
         android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
                 android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
@@ -46,7 +46,7 @@
         }
     }
 
-    static void onFingerUp(IBiometricsFingerprint daemon) {
+    public static void onFingerUp(IBiometricsFingerprint daemon) {
         android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
                 android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
                         daemon);
@@ -62,7 +62,7 @@
         }
     }
 
-    static void showUdfpsOverlay(int sensorId,
+    public static void showUdfpsOverlay(int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         if (udfpsOverlayController == null) {
             return;
@@ -74,7 +74,7 @@
         }
     }
 
-    static void hideUdfpsOverlay(int sensorId,
+    public static void hideUdfpsOverlay(int sensorId,
             @Nullable IUdfpsOverlayController udfpsOverlayController) {
         if (udfpsOverlayController == null) {
             return;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
new file mode 100644
index 0000000..33f5418
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -0,0 +1,112 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.common.ICancellationSignal;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
+
+public class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps {
+
+    private static final String TAG = "FingerprintEnrollClient";
+
+    @Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+    @Nullable private ICancellationSignal mCancellationSignal;
+    private final int mMaxTemplatesPerUser;
+
+    public FingerprintEnrollClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+            int statsModality, int sensorId,
+            @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) {
+        super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
+                0 /* timeoutSec */, statsModality, sensorId, true /* shouldVibrate */);
+        mUdfpsOverlayController = udfpsOvelayController;
+        mMaxTemplatesPerUser = maxTemplatesPerUser;
+    }
+
+    @Override
+    protected boolean hasReachedEnrollmentLimit() {
+        return FingerprintUtils.getInstance()
+                .getBiometricsForUser(getContext(), getTargetUserId()).size()
+                >= mMaxTemplatesPerUser;
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        if (mCancellationSignal != null) {
+            try {
+                mCancellationSignal.cancel();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception when requesting cancel", e);
+                onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                        0 /* vendorCode */);
+                mCallback.onClientFinished(this, false /* success */);
+            }
+        }
+    }
+
+    @Override
+    protected void startHalOperation() {
+        UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+        try {
+            getFreshDaemon().enroll(mSequentialId,
+                    HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception when requesting enroll", e);
+            onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    @Override
+    public void onPointerDown(int x, int y, float minor, float major) {
+        try {
+            getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send pointer down", e);
+        }
+    }
+
+    @Override
+    public void onPointerUp() {
+        try {
+            getFreshDaemon().onPointerUp(0 /* pointerId */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send pointer up", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
new file mode 100644
index 0000000..7db01ee
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java
@@ -0,0 +1,71 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.IGenerateChallengeCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.GenerateChallengeClient;
+
+/**
+ * Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface.
+ */
+public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<IFingerprint> {
+    private static final String TAG = "FingerprintGenerateChallengeClient";
+    private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
+
+    private final IGenerateChallengeCallback mGenerateChallengeCallback =
+            new IGenerateChallengeCallback.Stub() {
+        @Override
+        public void onChallengeGenerated(int sensorId, int userId, long challenge) {
+            try {
+                getListener().onChallengeGenerated(sensorId, challenge);
+                mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                        true /* success */);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to send challenge", e);
+                mCallback.onClientFinished(FingerprintGenerateChallengeClient.this,
+                        false /* success */);
+            }
+        }
+    };
+
+    public FingerprintGenerateChallengeClient(@NonNull Context context,
+            @NonNull LazyDaemon<IFingerprint> lazyDaemon,
+            @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener,
+            @NonNull String owner, int sensorId) {
+        super(context, lazyDaemon, token, listener, owner, sensorId);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().generateChallenge(getSensorId(), getTargetUserId(),
+                    CHALLENGE_TIMEOUT_SEC,
+                    mGenerateChallengeCallback);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to generateChallenge", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
new file mode 100644
index 0000000..0b59086
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -0,0 +1,360 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.SensorProps;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provider for a single instance of the {@link IFingerprint} HAL.
+ */
+public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider {
+
+    @NonNull private final Context mContext;
+    @NonNull private final String mHalInstanceName;
+    @NonNull private final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
+    @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
+    @NonNull private final Handler mHandler;
+    @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+    @Nullable private IUdfpsOverlayController mUdfpsOverlayController;
+
+    public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
+            @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+        mContext = context;
+        mHalInstanceName = halInstanceName;
+        mSensors = new SparseArray<>();
+        mLazyDaemon = this::getHalInstance;
+        mHandler = new Handler(Looper.getMainLooper());
+        mLockoutResetDispatcher = lockoutResetDispatcher;
+
+        for (SensorProps prop : props) {
+            final int sensorId = prop.commonProps.sensorId;
+
+            final FingerprintSensorPropertiesInternal internalProp =
+                    new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId,
+                            prop.commonProps.sensorStrength,
+                            prop.commonProps.maxEnrollmentsPerUser,
+                            prop.sensorType,
+                            true /* resetLockoutRequiresHardwareAuthToken */);
+            final Sensor sensor = new Sensor(getTag() + "/" + sensorId, mContext, mHandler,
+                    internalProp, gestureAvailabilityDispatcher);
+
+            mSensors.put(sensorId, sensor);
+            Slog.d(getTag(), "Added: " + internalProp);
+        }
+    }
+
+    private String getTag() {
+        return "FingerprintProvider/" + mHalInstanceName;
+    }
+
+    @Nullable
+    private synchronized IFingerprint getHalInstance() {
+        final IFingerprint daemon = IFingerprint.Stub.asInterface(
+                ServiceManager.waitForDeclaredService(mHalInstanceName));
+        if (daemon == null) {
+            Slog.e(getTag(), "Unable to get daemon");
+            return null;
+        }
+
+        try {
+            daemon.asBinder().linkToDeath(this, 0 /* flags */);
+        } catch (RemoteException e) {
+            Slog.e(getTag(), "Unable to linkToDeath", e);
+        }
+
+        for (int i = 0; i < mSensors.size(); i++) {
+            final int sensorId = mSensors.keyAt(i);
+            scheduleLoadAuthenticatorIds(sensorId);
+            scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser());
+        }
+
+        return daemon;
+    }
+
+    private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) {
+        if (!mSensors.contains(sensorId)) {
+            throw new IllegalStateException("Unable to schedule client: " + client
+                    + " for sensor: " + sensorId);
+        }
+        mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
+    }
+
+    private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client,
+            ClientMonitor.Callback callback) {
+        if (!mSensors.contains(sensorId)) {
+            throw new IllegalStateException("Unable to schedule client: " + client
+                    + " for sensor: " + sensorId);
+        }
+        mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback);
+    }
+
+    private void scheduleCreateSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId,
+            int userId) throws RemoteException {
+        // Note that per IFingerprint createSession contract, this method will block until all
+        // existing operations are canceled/finished. However, also note that this is fine, since
+        // this method "withoutHandler" means it should only ever be invoked from the worker thread,
+        // so callers will never be blocked.
+        mSensors.get(sensorId).createNewSession(daemon, sensorId, userId);
+    }
+
+    private void scheduleLoadAuthenticatorIdsWithoutHandler(int sensorId) {
+
+    }
+
+    private void scheduleLoadAuthenticatorIds(int sensorId) {
+
+    }
+
+    @Override
+    public boolean containsSensor(int sensorId) {
+        return mSensors.contains(sensorId);
+    }
+
+    @NonNull
+    @Override
+    public List<FingerprintSensorPropertiesInternal> getSensorProperties() {
+        final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+        for (int i = 0; i < mSensors.size(); i++) {
+            props.add(mSensors.valueAt(i).getSensorProperties());
+        }
+        return props;
+    }
+
+    @Override
+    public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during resetLockout, sensorId: " + sensorId);
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(
+                        mContext, mSensors.get(sensorId).getLazySession(), userId,
+                        mContext.getOpPackageName(), sensorId, hardwareAuthToken,
+                        mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
+                scheduleForSensor(sensorId, client);
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling resetLockout", e);
+            }
+        });
+    }
+
+    @Override
+    public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+            @NonNull IFingerprintServiceReceiver receiver, String opPackageName) {
+        mHandler.post(() -> {
+            final FingerprintGenerateChallengeClient client =
+                    new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token,
+                            new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId);
+            scheduleForSensor(sensorId, client);
+        });
+    }
+
+    @Override
+    public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+            @NonNull String opPackageName, long challenge) {
+        mHandler.post(() -> {
+            final FingerprintRevokeChallengeClient client =
+                    new FingerprintRevokeChallengeClient(mContext, mLazyDaemon, token,
+                            opPackageName, sensorId, challenge);
+            scheduleForSensor(sensorId, client);
+        });
+    }
+
+    @Override
+    public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
+            int userId, @NonNull IFingerprintServiceReceiver receiver,
+            @NonNull String opPackageName, @Nullable Surface surface) {
+        mHandler.post(() -> {
+            final IFingerprint daemon = getHalInstance();
+            if (daemon == null) {
+                Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId);
+
+                try {
+                    receiver.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                            0 /* vendorCode */);
+                } catch (RemoteException e) {
+                    Slog.e(getTag(), "Unable to send HW_UNAVAILABLE", e);
+                }
+                return;
+            }
+
+            try {
+                if (!mSensors.get(sensorId).hasSessionForUser(userId)) {
+                    scheduleCreateSessionWithoutHandler(daemon, sensorId, userId);
+                }
+
+                final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
+                        .maxEnrollmentsPerUser;
+                final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext,
+                        mSensors.get(sensorId).getLazySession(), token,
+                        new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
+                        opPackageName, FingerprintUtils.getInstance(),
+                        BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
+                        mUdfpsOverlayController, maxTemplatesPerUser);
+                scheduleForSensor(sensorId, client, new ClientMonitor.Callback() {
+                    @Override
+                    public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor,
+                            boolean success) {
+                        if (success) {
+                            scheduleLoadAuthenticatorIdsWithoutHandler(sensorId);
+                        }
+                    }
+                });
+            } catch (RemoteException e) {
+                Slog.e(getTag(), "Remote exception when scheduling enroll", e);
+            }
+        });
+    }
+
+    @Override
+    public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
+
+    }
+
+    @Override
+    public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+            @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+            @Nullable Surface surface, int statsClient) {
+
+    }
+
+    @Override
+    public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+            int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
+            @NonNull String opPackageName, boolean restricted, int statsClient,
+            boolean isKeyguard) {
+
+    }
+
+    @Override
+    public void startPreparedClient(int sensorId, int cookie) {
+
+    }
+
+    @Override
+    public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
+
+    }
+
+    @Override
+    public void scheduleRemove(int sensorId, @NonNull IBinder token,
+            @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+            @NonNull String opPackageName) {
+
+    }
+
+    @Override
+    public void scheduleInternalCleanup(int userId, int sensorId) {
+
+    }
+
+    @Override
+    public boolean isHardwareDetected(int sensorId) {
+        return false;
+    }
+
+    @Override
+    public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
+
+    }
+
+    @NonNull
+    @Override
+    public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
+        return new ArrayList<>();
+    }
+
+    @Override
+    public int getLockoutModeForUser(int sensorId, int userId) {
+        return 0;
+    }
+
+    @Override
+    public long getAuthenticatorId(int sensorId, int userId) {
+        return 0;
+    }
+
+    @Override
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
+
+    }
+
+    @Override
+    public void onPointerUp(int sensorId) {
+
+    }
+
+    @Override
+    public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
+        mUdfpsOverlayController = controller;
+    }
+
+    @Override
+    public void dumpProto(int sensorId, @NonNull FileDescriptor fd) {
+
+    }
+
+    @Override
+    public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
+
+    }
+
+    @Override
+    public void binderDied() {
+
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
new file mode 100644
index 0000000..bc35ad4
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * Fingerprint-specific resetLockout client for the {@link IFingerprint} AIDL HAL interface.
+ * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is
+ * cleared.
+ */
+public class FingerprintResetLockoutClient extends ClientMonitor<ISession> {
+
+    private static final String TAG = "FingerprintResetLockoutClient";
+
+    private final HardwareAuthToken mHardwareAuthToken;
+    private final LockoutCache mLockoutCache;
+    private final LockoutResetDispatcher mLockoutResetDispatcher;
+
+    public FingerprintResetLockoutClient(@NonNull Context context,
+            @NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId,
+            @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+        super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
+                0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
+        mLockoutCache = lockoutTracker;
+        mLockoutResetDispatcher = lockoutResetDispatcher;
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().resetLockout(mSequentialId, mHardwareAuthToken);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to reset lockout", e);
+            mCallback.onClientFinished(this, false /* success */);
+        }
+    }
+
+    void onLockoutCleared() {
+        mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_NONE);
+        mLockoutResetDispatcher.notifyLockoutResetCallbacks(getSensorId());
+        mCallback.onClientFinished(this, true /* success */);
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
new file mode 100644
index 0000000..e97dbe7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.IRevokeChallengeCallback;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.RevokeChallengeClient;
+
+/**
+ * Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface.
+ */
+public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<IFingerprint> {
+
+    private static final String TAG = "FingerpirntRevokeChallengeClient";
+
+    private final long mChallenge;
+
+    private final IRevokeChallengeCallback mRevokeChallengeCallback =
+            new IRevokeChallengeCallback.Stub() {
+        @Override
+        public void onChallengeRevoked(int sensorId, int userId, long challenge) {
+            final boolean success = challenge == mChallenge;
+            mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success);
+        }
+    };
+
+    public FingerprintRevokeChallengeClient(
+            @NonNull Context context,
+            @NonNull LazyDaemon<IFingerprint> lazyDaemon,
+            @NonNull IBinder token,
+            @NonNull String owner, int sensorId, long challenge) {
+        super(context, lazyDaemon, token, owner, sensorId);
+        mChallenge = challenge;
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            getFreshDaemon().revokeChallenge(getSensorId(), getTargetUserId(), mChallenge,
+                    mRevokeChallengeCallback);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to revokeChallenge", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
new file mode 100644
index 0000000..2abbcb0
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java
@@ -0,0 +1,47 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.util.SparseIntArray;
+
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+/**
+ * For a single sensor, caches lockout states for all users.
+ */
+public class LockoutCache implements LockoutTracker {
+
+    // Map of userId to LockoutMode
+    private final SparseIntArray mUserLockoutStates;
+
+    LockoutCache() {
+        mUserLockoutStates = new SparseIntArray();
+    }
+
+    public void setLockoutModeForUser(int userId, @LockoutMode int mode) {
+        synchronized (this) {
+            mUserLockoutStates.put(userId, mode);
+        }
+    }
+
+    @Override
+    public int getLockoutModeForUser(int userId) {
+        synchronized (this) {
+            return mUserLockoutStates.get(userId, LOCKOUT_NONE);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
new file mode 100644
index 0000000..2ed695d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -0,0 +1,273 @@
+/*
+ * 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.server.biometrics.sensors.fingerprint.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.biometrics.fingerprint.Error;
+import android.hardware.biometrics.fingerprint.IFingerprint;
+import android.hardware.biometrics.fingerprint.ISession;
+import android.hardware.biometrics.fingerprint.ISessionCallback;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.keymaster.HardwareAuthToken;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Maintains the state of a single sensor within an instance of the
+ * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
+ */
+class Sensor {
+    @NonNull private final String mTag;
+    @NonNull private final Context mContext;
+    @NonNull private final Handler mHandler;
+    @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
+    @NonNull private final BiometricScheduler mScheduler;
+    @NonNull private final LockoutCache mLockoutCache;
+
+    @Nullable private Session mCurrentSession; // TODO: Death recipient
+    @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession;
+
+    private static class Session {
+        @NonNull private final String mTag;
+        @NonNull private final ISession mSession;
+        private final int mUserId;
+        private final ISessionCallback mSessionCallback;
+
+        Session(@NonNull String tag, @NonNull ISession session, int userId,
+                @NonNull ISessionCallback sessionCallback) {
+            mTag = tag;
+            mSession = session;
+            mUserId = userId;
+            mSessionCallback = sessionCallback;
+            Slog.d(mTag, "New session created for user: " + userId);
+        }
+    }
+
+    Sensor(@NonNull String tag, @NonNull Context context, @NonNull Handler handler,
+            @NonNull FingerprintSensorPropertiesInternal sensorProperties,
+            @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
+        mTag = tag;
+        mContext = context;
+        mHandler = handler;
+        mSensorProperties = sensorProperties;
+        mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher);
+        mLockoutCache = new LockoutCache();
+        mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null;
+    }
+
+    @NonNull ClientMonitor.LazyDaemon<ISession> getLazySession() {
+        return mLazySession;
+    }
+
+    @NonNull FingerprintSensorPropertiesInternal getSensorProperties() {
+        return mSensorProperties;
+    }
+
+    boolean hasSessionForUser(int userId) {
+        return mCurrentSession != null && mCurrentSession.mUserId == userId;
+    }
+
+    void createNewSession(@NonNull IFingerprint daemon, int sensorId, int userId)
+            throws RemoteException {
+        final ISessionCallback callback = new ISessionCallback.Stub() {
+            @Override
+            public void onStateChanged(int cookie, byte state) {
+
+            }
+
+            @Override
+            public void onAcquired(byte info, int vendorCode) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AcquisitionClient)) {
+                        Slog.e(mTag, "onAcquired for non-acquisition client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+                    acquisitionClient.onAcquired(info, vendorCode);
+                });
+            }
+
+            @Override
+            public void onError(byte error, int vendorCode) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    Slog.d(mTag, "onError"
+                            + ", client: " + Utils.getClientName(client)
+                            + ", error: " + error
+                            + ", vendorCode: " + vendorCode);
+                    if (!(client instanceof Interruptable)) {
+                        Slog.e(mTag, "onError for non-error consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final Interruptable interruptable = (Interruptable) client;
+                    interruptable.onError(error, vendorCode);
+
+                    if (error == Error.HW_UNAVAILABLE) {
+                        Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE");
+                        mCurrentSession = null;
+                    }
+                });
+            }
+
+            @Override
+            public void onEnrollmentProgress(int enrollmentId, int remaining) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintEnrollClient)) {
+                        Slog.e(mTag, "onEnrollmentProgress for non-enroll client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final int currentUserId = client.getTargetUserId();
+                    final CharSequence name = FingerprintUtils.getInstance()
+                            .getUniqueName(mContext, currentUserId);
+                    final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId);
+
+                    final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
+                    enrollClient.onEnrollResult(fingerprint, remaining);
+                });
+            }
+
+            @Override
+            public void onAuthenticationSucceeded(int enrollmentId, HardwareAuthToken hat) {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AuthenticationConsumer)) {
+                        Slog.e(mTag, "onAuthenticationSucceeded for non-authentication consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AuthenticationConsumer authenticationConsumer =
+                            (AuthenticationConsumer) client;
+                    final Fingerprint fp = new Fingerprint("", enrollmentId, sensorId);
+                    final byte[] byteArray = HardwareAuthTokenUtils.toByteArray(hat);
+                    final ArrayList<Byte> byteList = new ArrayList<>();
+                    for (byte b : byteArray) {
+                        byteList.add(b);
+                    }
+
+                    authenticationConsumer.onAuthenticated(fp, true /* authenticated */, byteList);
+                });
+            }
+
+            @Override
+            public void onAuthenticationFailed() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof AuthenticationConsumer)) {
+                        Slog.e(mTag, "onAuthenticationFailed for non-authentication consumer: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final AuthenticationConsumer authenticationConsumer =
+                            (AuthenticationConsumer) client;
+                    final Fingerprint fp = new Fingerprint("", 0 /* enrollmentId */, sensorId);
+                    authenticationConsumer
+                            .onAuthenticated(fp, false /* authenticated */, null /* hat */);
+                });
+            }
+
+            @Override
+            public void onLockoutTimed(long durationMillis) {
+
+            }
+
+            @Override
+            public void onLockoutPermanent() {
+
+            }
+
+            @Override
+            public void onLockoutCleared() {
+                mHandler.post(() -> {
+                    final ClientMonitor<?> client = mScheduler.getCurrentClient();
+                    if (!(client instanceof FingerprintResetLockoutClient)) {
+                        Slog.e(mTag, "onLockoutCleared for non-resetLockout client: "
+                                + Utils.getClientName(client));
+                        return;
+                    }
+
+                    final FingerprintResetLockoutClient resetLockoutClient =
+                            (FingerprintResetLockoutClient) client;
+                    resetLockoutClient.onLockoutCleared();
+                });
+            }
+
+            @Override
+            public void onInteractionDetected() {
+
+            }
+
+            @Override
+            public void onEnrollmentsEnumerated(int[] enrollmentIds) {
+
+            }
+
+            @Override
+            public void onEnrollmentsRemoved(int[] enrollmentIds) {
+
+            }
+
+            @Override
+            public void onAuthenticatorIdRetrieved(long authenticatorId) {
+
+            }
+
+            @Override
+            public void onAuthenticatorIdInvalidated() {
+
+            }
+        };
+
+        final ISession newSession = daemon.createSession(sensorId, userId, callback);
+        mCurrentSession = new Session(mTag, newSession, userId, callback);
+    }
+
+    @NonNull BiometricScheduler getScheduler() {
+        return mScheduler;
+    }
+
+    @NonNull LockoutCache getLockoutCache() {
+        return mLockoutCache;
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f890f57..fedcfa5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -68,6 +68,7 @@
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
 
 import org.json.JSONArray;
 import org.json.JSONException;
@@ -245,7 +246,7 @@
             mHandler.post(() -> {
                 final ClientMonitor<?> client = mScheduler.getCurrentClient();
                 Slog.d(TAG, "handleError"
-                        + ", client: " + (client != null ? client.getOwnerString() : null)
+                        + ", client: " + Utils.getClientName(client)
                         + ", error: " + error
                         + ", vendorCode: " + vendorCode);
                 if (!(client instanceof Interruptable)) {
@@ -517,7 +518,7 @@
 
     @Override
     public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
-            @NonNull String opPackageName) {
+            @NonNull String opPackageName, long challenge) {
         mHandler.post(() -> {
             final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
                     mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -635,6 +636,11 @@
     }
 
     @Override
+    public void scheduleInternalCleanup(int userId, int sensorId) {
+        scheduleInternalCleanup(userId);
+    }
+
+    @Override
     public boolean isHardwareDetected(int sensorId) {
         final IBiometricsFingerprint daemon = getDaemon();
         return daemon != null;
@@ -664,25 +670,25 @@
     }
 
     @Override
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         final ClientMonitor<?> client = mScheduler.getCurrentClient();
         if (!(client instanceof Udfps)) {
             Slog.w(TAG, "onFingerDown received during client: " + client);
             return;
         }
         final Udfps udfps = (Udfps) client;
-        udfps.onFingerDown(x, y, minor, major);
+        udfps.onPointerDown(x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         final ClientMonitor<?> client = mScheduler.getCurrentClient();
         if (!(client instanceof Udfps)) {
             Slog.w(TAG, "onFingerDown received during client: " + client);
             return;
         }
         final Udfps udfps = (Udfps) client;
-        udfps.onFingerUp();
+        udfps.onPointerUp();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 5dda5a8..e4933e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -461,7 +461,7 @@
     }
 
     @Override
-    public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
+    public void onPointerDown(int sensorId, int x, int y, float minor, float major) {
         mHandler.post(() -> {
             Slog.d(TAG, "onFingerDown");
             final AuthenticationConsumer lastAuthenticatedConsumer =
@@ -508,7 +508,7 @@
     }
 
     @Override
-    public void onFingerUp(int sensorId) {
+    public void onPointerUp(int sensorId) {
         mHandler.post(() -> {
             Slog.d(TAG, "onFingerUp");
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 0658f95..46605d1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -33,6 +33,8 @@
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 import java.util.ArrayList;
 
@@ -138,12 +140,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index cad2214..4747488 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -32,6 +32,8 @@
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 import java.util.ArrayList;
 
@@ -93,12 +95,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index b1030bf..975ac3d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -30,6 +30,8 @@
 import com.android.server.biometrics.sensors.BiometricUtils;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.fingerprint.Udfps;
+import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper;
 
 /**
  * Fingerprint-specific enroll client supporting the
@@ -97,12 +99,12 @@
     }
 
     @Override
-    public void onFingerDown(int x, int y, float minor, float major) {
+    public void onPointerDown(int x, int y, float minor, float major) {
         UdfpsHelper.onFingerDown(getFreshDaemon(), x, y, minor, major);
     }
 
     @Override
-    public void onFingerUp() {
+    public void onPointerUp() {
         UdfpsHelper.onFingerUp(getFreshDaemon());
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 01fa9e7..8625a6f 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -47,7 +47,6 @@
 import android.net.NetworkUtils;
 import android.net.SocketKeepalive.InvalidSocketException;
 import android.net.TcpKeepalivePacketData;
-import android.net.util.IpUtils;
 import android.net.util.KeepaliveUtils;
 import android.os.Binder;
 import android.os.Handler;
@@ -63,6 +62,7 @@
 import com.android.internal.R;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.IpUtils;
 
 import java.io.FileDescriptor;
 import java.net.InetAddress;
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 09c01d7..1ed6b35 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -2152,7 +2152,11 @@
                 break;
         }
 
-        // Prepare arguments for mtpd.
+        // Prepare arguments for mtpd. MTU/MRU calculated conservatively. Only IPv4 supported
+        // because LegacyVpn.
+        // 1500 - 60 (Carrier-internal IPv6 + UDP + GTP) - 10 (PPP) - 16 (L2TP) - 8 (UDP)
+        //   - 77 (IPsec w/ SHA-2 512, 256b trunc-len, AES-CBC) - 8 (UDP encap) - 20 (IPv4)
+        //   - 28 (464xlat)
         String[] mtpd = null;
         switch (profile.type) {
             case VpnProfile.TYPE_PPTP:
@@ -2160,7 +2164,7 @@
                     iface, "pptp", profile.server, "1723",
                     "name", profile.username, "password", profile.password,
                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
-                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                    "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
                     (profile.mppe ? "+mppe" : "nomppe"),
                 };
                 break;
@@ -2170,7 +2174,7 @@
                     iface, "l2tp", profile.server, "1701", profile.l2tpSecret,
                     "name", profile.username, "password", profile.password,
                     "linkname", "vpn", "refuse-eap", "nodefaultroute",
-                    "usepeerdns", "idle", "1800", "mtu", "1400", "mru", "1400",
+                    "usepeerdns", "idle", "1800", "mtu", "1270", "mru", "1270",
                 };
                 break;
         }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index af62aeb..ca94efc 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1768,7 +1768,9 @@
             final int callingUid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+                synchronized (mSyncRoot) {
+                    return mLogicalDisplayMapper.getDisplayIdsLocked(callingUid);
+                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 507a265..858cce4 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -969,6 +969,10 @@
         }
 
         private int findMatchingModeIdLocked(int configId) {
+            if (configId < 0 || configId >= mDisplayConfigs.length) {
+                Slog.e(TAG, "Invalid display config index " + configId);
+                return NO_DISPLAY_MODE_ID;
+            }
             SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
             for (int i = 0; i < mSupportedModes.size(); i++) {
                 DisplayModeRecord record = mSupportedModes.valueAt(i);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6536457..3f4ddea 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2089,6 +2089,16 @@
         }
     }
 
+    // Native callback
+    private void notifyUntrustedTouch(String packageName) {
+        // TODO(b/169067926): Remove toast after gathering feedback on dogfood.
+        DisplayThread.getHandler().post(() ->
+                Toast.makeText(mContext,
+                        "Touch obscured by " + packageName
+                                + " will be blocked. Check go/s-untrusted-touches",
+                        Toast.LENGTH_SHORT).show());
+    }
+
     // Native callback.
     private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token,
             String reason) {
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f72fee6..cdb73d8 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -216,7 +216,7 @@
     private final LocalService mLocalService;
 
     private final GeofenceManager mGeofenceManager;
-    @Nullable private volatile GnssManagerService mGnssManagerService = null;
+    private volatile @Nullable GnssManagerService mGnssManagerService = null;
     private GeocoderProxy mGeocodeProvider;
 
     @GuardedBy("mLock")
@@ -604,7 +604,8 @@
                 || !request.getWorkSource().isEmpty();
         if (usesSystemApi
                 && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
-            throw new SecurityException("PendingIntent location requests may not use system APIs");
+            throw new SecurityException(
+                    "PendingIntent location requests may not use system APIs: " + request);
         }
 
         request = validateLocationRequest(request, identity);
@@ -1091,19 +1092,6 @@
     }
 
     @Override
-    @NonNull
-    public List<LocationRequest> getTestProviderCurrentRequests(String provider) {
-        mContext.enforceCallingOrSelfPermission(permission.READ_DEVICE_CONFIG, null);
-
-        LocationProviderManager manager = getLocationProviderManager(provider);
-        if (manager == null) {
-            throw new IllegalArgumentException("provider doesn't exist: " + provider);
-        }
-
-        return manager.getMockProviderRequests();
-    }
-
-    @Override
     public int handleShellCommand(ParcelFileDescriptor in, ParcelFileDescriptor out,
             ParcelFileDescriptor err, String[] args) {
         return new LocationShellCommand(this).exec(
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 3b3c6f0..5206571 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -1391,16 +1391,6 @@
         }
     }
 
-    public List<LocationRequest> getMockProviderRequests() {
-        synchronized (mLock) {
-            if (!mProvider.isMock()) {
-                throw new IllegalArgumentException(mName + " provider is not a test provider");
-            }
-
-            return mProvider.getCurrentRequest().getLocationRequests();
-        }
-    }
-
     @Nullable
     public Location getLastLocation(CallerIdentity identity, @PermissionLevel int permissionLevel,
             boolean ignoreLocationSettings) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index dcfd3f2..35d3621 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3900,7 +3900,7 @@
                         // quick check: if this uid doesn't have INTERNET permission, it
                         // doesn't have network access anyway, so it is a waste to mess
                         // with it here.
-                        if (hasInternetPermissionUL(uid)) {
+                        if (hasInternetPermissionUL(uid) && !isUidForegroundOnRestrictPowerUL(uid)) {
                             uidRules.put(uid, FIREWALL_RULE_DENY);
                         }
                     }
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index d202a2a..5646c75 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -30,6 +30,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.CollectionUtils;
@@ -94,39 +95,41 @@
         // also needed to track CBRS.
         final List<Integer> newSubs = getActiveSubIdList(mSubscriptionManager);
 
-        for (final int subId : newSubs) {
-            final RatTypeListener match = CollectionUtils.find(mRatListeners,
-                    it -> it.mSubId == subId);
-            if (match != null) continue;
+        // IMSI is needed for every newly added sub. Listener stores subscriberId into it to
+        // prevent binder call to telephony when querying RAT. Keep listener registration with empty
+        // IMSI is meaningless since the RAT type changed is ambiguous for multi-SIM if reported
+        // with empty IMSI. So filter the subs w/o a valid IMSI to prevent such registration.
+        final List<Pair<Integer, String>> filteredNewSubs =
+                CollectionUtils.mapNotNull(newSubs, subId -> {
+                    final String subscriberId = mTeleManager.getSubscriberId(subId);
+                    return TextUtils.isEmpty(subscriberId) ? null : new Pair(subId, subscriberId);
+                });
 
-            // Create listener for every newly added sub. Also store subscriberId into it to
-            // prevent binder call to telephony when querying RAT. If the subscriberId is empty
-            // for any reason, such as SIM PIN locked, skip registration.
-            // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
-            // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
-            // with active sub list once all subscriberIds are ready.
-            final String subscriberId = mTeleManager.getSubscriberId(subId);
-            if (TextUtils.isEmpty(subscriberId)) {
-                Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
-                        + subId + ", skip listener registration");
+        for (final Pair<Integer, String> sub : filteredNewSubs) {
+            // Fully match listener with subId and IMSI, since in some rare cases, IMSI might be
+            // suddenly change regardless of subId, such as switch IMSI feature in modem side.
+            // If that happens, register new listener with new IMSI and remove old one later.
+            if (CollectionUtils.find(mRatListeners,
+                    it -> it.equalsKey(sub.first, sub.second)) != null) {
                 continue;
             }
+
             final RatTypeListener listener =
-                    new RatTypeListener(mExecutor, this, subId, subscriberId);
+                    new RatTypeListener(mExecutor, this, sub.first, sub.second);
             mRatListeners.add(listener);
 
             // Register listener to the telephony manager that associated with specific sub.
-            mTeleManager.createForSubscriptionId(subId)
+            mTeleManager.createForSubscriptionId(sub.first)
                     .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
-            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
+            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + sub.first);
         }
 
         for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
-            // If the new list contains the subId of the listener, keeps it.
-            final Integer match = CollectionUtils.find(newSubs, it -> it == listener.mSubId);
-            if (match != null) continue;
-
-            handleRemoveRatTypeListener(listener);
+            // If there is no subId and IMSI matched the listener, removes it.
+            if (CollectionUtils.find(filteredNewSubs,
+                    it -> listener.equalsKey(it.first, it.second)) == null) {
+                handleRemoveRatTypeListener(listener);
+            }
         }
     }
 
@@ -232,5 +235,9 @@
         public int getSubId() {
             return mSubId;
         }
+
+        boolean equalsKey(int subId, @NonNull String subscriberId) {
+            return mSubId == subId && TextUtils.equals(mSubscriberId, subscriberId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 74b7bd7..4040f41 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -833,6 +833,7 @@
 
     public void onUserSwitched(int user) {
         if (DEBUG) Slog.d(TAG, "onUserSwitched u=" + user);
+        unbindOtherUserServices(user);
         rebindServices(true, user);
     }
 
@@ -1219,6 +1220,27 @@
         bindToServices(componentsToBind);
     }
 
+    /**
+     * Called when user switched to unbind all services from other users.
+     */
+    @VisibleForTesting
+    void unbindOtherUserServices(int currentUser) {
+        final SparseArray<Set<ComponentName>> componentsToUnbind = new SparseArray<>();
+
+        synchronized (mMutex) {
+            final Set<ManagedServiceInfo> removableBoundServices = getRemovableConnectedServices();
+            for (ManagedServiceInfo info : removableBoundServices) {
+                if (info.userid != currentUser) {
+                    Set<ComponentName> toUnbind =
+                            componentsToUnbind.get(info.userid, new ArraySet<>());
+                    toUnbind.add(info.component);
+                    componentsToUnbind.put(info.userid, toUnbind);
+                }
+            }
+        }
+        unbindFromServices(componentsToUnbind);
+    }
+
     protected void unbindFromServices(SparseArray<Set<ComponentName>> componentsToUnbind) {
         for (int i = 0; i < componentsToUnbind.size(); i++) {
             final int userId = componentsToUnbind.keyAt(i);
@@ -1264,7 +1286,8 @@
     /**
      * Version of registerService that takes the name of a service component to bind to.
      */
-    private void registerService(final ComponentName name, final int userid) {
+    @VisibleForTesting
+    void registerService(final ComponentName name, final int userid) {
         synchronized (mMutex) {
             registerServiceLocked(name, userid);
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 67ef029..23798c0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -60,6 +60,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -139,6 +140,7 @@
 import android.app.UriGrantsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
+import android.app.compat.CompatChanges;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.app.usage.UsageEvents;
@@ -407,9 +409,19 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
 
+    /**
+     * Activity starts coming from broadcast receivers or services in response to notification and
+     * notification action clicks will be blocked for UX and performance reasons. Instead start the
+     * activity directly from the PendingIntent.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long NOTIFICATION_TRAMPOLINE_BLOCK = 167676448L;
+
     private IActivityManager mAm;
     private ActivityTaskManagerInternal mAtm;
     private ActivityManager mActivityManager;
+    private ActivityManagerInternal mAmi;
     private IPackageManager mPackageManager;
     private PackageManager mPackageManagerClient;
     AudioManager mAudioManager;
@@ -1886,7 +1898,7 @@
             DevicePolicyManagerInternal dpm, IUriGrantsManager ugm,
             UriGrantsManagerInternal ugmInternal, AppOpsManager appOps, UserManager userManager,
             NotificationHistoryManager historyManager, StatsManager statsManager,
-            TelephonyManager telephonyManager) {
+            TelephonyManager telephonyManager, ActivityManagerInternal ami) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -1908,6 +1920,7 @@
         mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
         mCompanionManager = companionManager;
         mActivityManager = activityManager;
+        mAmi = ami;
         mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class);
         mDpm = dpm;
         mUm = userManager;
@@ -2186,7 +2199,8 @@
                 new NotificationHistoryManager(getContext(), handler),
                 mStatsManager = (StatsManager) getContext().getSystemService(
                         Context.STATS_MANAGER),
-                getContext().getSystemService(TelephonyManager.class));
+                getContext().getSystemService(TelephonyManager.class),
+                LocalServices.getService(ActivityManagerInternal.class));
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -5228,8 +5242,8 @@
             if (!summaries.containsKey(pkg)) {
                 // Add summary
                 final ApplicationInfo appInfo =
-                       adjustedSbn.getNotification().extras.getParcelable(
-                               Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                        adjustedSbn.getNotification().extras.getParcelable(
+                                Notification.EXTRA_BUILDER_APPLICATION_INFO);
                 final Bundle extras = new Bundle();
                 extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
                 final String channelId = notificationRecord.getChannel().getId();
@@ -5265,11 +5279,11 @@
                         notificationRecord.getIsAppImportanceLocked());
                 summaries.put(pkg, summarySbn.getKey());
             }
-        }
-        if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
-                summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
-                true)) {
-            mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+                    summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
+                    true)) {
+                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord, isAppForeground));
+            }
         }
     }
 
@@ -6008,13 +6022,17 @@
                 + " cannot post for pkg " + targetPkg + " in user " + userId);
     }
 
+    public boolean hasFlag(final int flags, final int flag) {
+        return (flags & flag) != 0;
+    }
     /**
      * Checks if a notification can be posted. checks rate limiter, snooze helper, and blocking.
      *
      * Has side effects.
      */
-    private boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
+    boolean checkDisqualifyingFeatures(int userId, int uid, int id, String tag,
             NotificationRecord r, boolean isAutogroup) {
+        Notification n = r.getNotification();
         final String pkg = r.getSbn().getPackageName();
         final boolean isSystemNotification =
                 isUidSystemOrPhone(uid) || ("android".equals(pkg));
@@ -6023,71 +6041,101 @@
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
-            synchronized (mNotificationLock) {
-                final int callingUid = Binder.getCallingUid();
-                if (mNotificationsByKey.get(r.getSbn().getKey()) == null
-                        && isCallerInstantApp(callingUid, userId)) {
-                    // Ephemeral apps have some special constraints for notifications.
-                    // They are not allowed to create new notifications however they are allowed to
-                    // update notifications created by the system (e.g. a foreground service
-                    // notification).
-                    throw new SecurityException("Instant app " + pkg
-                            + " cannot create notifications");
-                }
+            final int callingUid = Binder.getCallingUid();
+            if (mNotificationsByKey.get(r.getSbn().getKey()) == null
+                    && isCallerInstantApp(callingUid, userId)) {
+                // Ephemeral apps have some special constraints for notifications.
+                // They are not allowed to create new notifications however they are allowed to
+                // update notifications created by the system (e.g. a foreground service
+                // notification).
+                throw new SecurityException("Instant app " + pkg
+                        + " cannot create notifications");
+            }
 
-                // rate limit updates that aren't completed progress notifications
-                if (mNotificationsByKey.get(r.getSbn().getKey()) != null
-                        && !r.getNotification().hasCompletedProgress()
-                        && !isAutogroup) {
+            // rate limit updates that aren't completed progress notifications
+            if (mNotificationsByKey.get(r.getSbn().getKey()) != null
+                    && !r.getNotification().hasCompletedProgress()
+                    && !isAutogroup) {
 
-                    final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
-                    if (appEnqueueRate > mMaxPackageEnqueueRate) {
-                        mUsageStats.registerOverRateQuota(pkg);
-                        final long now = SystemClock.elapsedRealtime();
-                        if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
-                            Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
-                                    + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
-                            mLastOverRateLogTime = now;
-                        }
-                        return false;
+                final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg);
+                if (appEnqueueRate > mMaxPackageEnqueueRate) {
+                    mUsageStats.registerOverRateQuota(pkg);
+                    final long now = SystemClock.elapsedRealtime();
+                    if ((now - mLastOverRateLogTime) > MIN_PACKAGE_OVERRATE_LOG_INTERVAL) {
+                        Slog.e(TAG, "Package enqueue rate is " + appEnqueueRate
+                                + ". Shedding " + r.getSbn().getKey() + ". package=" + pkg);
+                        mLastOverRateLogTime = now;
                     }
+                    return false;
                 }
+            }
 
-                // limit the number of non-fgs outstanding notificationrecords an app can have
-                if (!r.getNotification().isForegroundService()) {
-                    int count = getNotificationCountLocked(pkg, userId, id, tag);
-                    if (count >= MAX_PACKAGE_NOTIFICATIONS) {
-                        mUsageStats.registerOverCountQuota(pkg);
-                        Slog.e(TAG, "Package has already posted or enqueued " + count
-                                + " notifications.  Not showing more.  package=" + pkg);
-                        return false;
-                    }
+            // limit the number of non-fgs outstanding notificationrecords an app can have
+            if (!n.isForegroundService()) {
+                int count = getNotificationCountLocked(pkg, userId, id, tag);
+                if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+                    mUsageStats.registerOverCountQuota(pkg);
+                    Slog.e(TAG, "Package has already posted or enqueued " + count
+                            + " notifications.  Not showing more.  package=" + pkg);
+                    return false;
                 }
             }
         }
 
-        synchronized (mNotificationLock) {
-            // snoozed apps
-            if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
-                MetricsLogger.action(r.getLogMaker()
-                        .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
-                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
-                mNotificationRecordLogger.log(
-                        NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
-                        r);
-                if (DBG) {
-                    Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+        // bubble or inline reply that's immutable?
+        if (n.getBubbleMetadata() != null
+                && n.getBubbleMetadata().getIntent() != null
+                && hasFlag(mAmi.getPendingIntentFlags(
+                        n.getBubbleMetadata().getIntent().getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+            throw new IllegalArgumentException(r.getKey() + " Not posted."
+                    + " PendingIntents attached to bubbles must be mutable");
+        }
+
+        if (n.actions != null) {
+            for (Notification.Action action : n.actions) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to actions with remote"
+                            + " inputs must be mutable");
                 }
-                mSnoozeHelper.update(userId, r);
-                handleSavePolicyFile();
-                return false;
             }
+        }
+
+        if (r.getSystemGeneratedSmartActions() != null) {
+            for (Notification.Action action : r.getSystemGeneratedSmartActions()) {
+                if ((action.getRemoteInputs() != null || action.getDataOnlyRemoteInputs() != null)
+                        && hasFlag(mAmi.getPendingIntentFlags(action.actionIntent.getTarget()),
+                        PendingIntent.FLAG_IMMUTABLE)) {
+                    throw new IllegalArgumentException(r.getKey() + " Not posted."
+                            + " PendingIntents attached to contextual actions with remote inputs"
+                            + " must be mutable");
+                }
+            }
+        }
+
+        // snoozed apps
+        if (mSnoozeHelper.isSnoozed(userId, pkg, r.getKey())) {
+            MetricsLogger.action(r.getLogMaker()
+                    .setType(MetricsProto.MetricsEvent.TYPE_UPDATE)
+                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED));
+            mNotificationRecordLogger.log(
+                    NotificationRecordLogger.NotificationEvent.NOTIFICATION_NOT_POSTED_SNOOZED,
+                    r);
+            if (DBG) {
+                Slog.d(TAG, "Ignored enqueue for snoozed notification " + r.getKey());
+            }
+            mSnoozeHelper.update(userId, r);
+            handleSavePolicyFile();
+            return false;
+        }
 
 
-            // blocked apps
-            if (isBlocked(r, mUsageStats)) {
-                return false;
-            }
+        // blocked apps
+        if (isBlocked(r, mUsageStats)) {
+            return false;
         }
 
         return true;
@@ -7138,8 +7186,15 @@
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
                         if (mNotificationsByKey.get(record.getKey()) != null) {
+                            // Vibrator checks the appops for the op package, not the caller,
+                            // so we need to add the bypass dnd flag to be heard. it's ok to
+                            // always add this flag here because we've already checked that we can
+                            // bypass dnd
+                            AudioAttributes.Builder aab =
+                                    new AudioAttributes.Builder(record.getAudioAttributes())
+                                    .setFlags(FLAG_BYPASS_INTERRUPTION_POLICY);
                             mVibrator.vibrate(record.getSbn().getUid(), record.getSbn().getOpPkg(),
-                                    effect, "Notification (delayed)", record.getAudioAttributes());
+                                    effect, "Notification (delayed)", aab.build());
                         } else {
                             Slog.e(TAG, "No vibration for canceled notification : "
                                     + record.getKey());
@@ -10005,7 +10060,7 @@
      * TODO(b/161957908): Remove dogfooder toast.
      */
     private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
-        private Set<String> mPackagesShown = new ArraySet<>();
+        private final Set<String> mPackagesShown = new ArraySet<>();
 
         @Override
         public IBinder getToken() {
@@ -10013,20 +10068,25 @@
         }
 
         @Override
-        public void onExclusiveTokenActivityStart(String packageName) {
-            Slog.w(TAG, "Indirect notification activity start from " + packageName);
-            boolean isFirstOccurrence = mPackagesShown.add(packageName);
-            if (!isFirstOccurrence) {
-                return;
+        public boolean isActivityStartAllowed(int uid, String packageName) {
+            boolean block = CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
+            if (block || mPackagesShown.add(packageName)) {
+                mUiHandler.post(() ->
+                        Toast.makeText(getUiContext(),
+                                "Indirect activity start from "
+                                        + packageName + ". "
+                                        + "This will be blocked in S.\n"
+                                        + "See go/s-trampolines.",
+                                Toast.LENGTH_LONG).show());
             }
-
-            mUiHandler.post(() ->
-                    Toast.makeText(getUiContext(),
-                            "Indirect activity start from "
-                                    + packageName + ". "
-                                    + "This will be blocked in S.\n"
-                                    + "See go/s-trampolines.",
-                            Toast.LENGTH_LONG).show());
+            String message =
+                    "Indirect notification activity start (trampoline) from " + packageName;
+            if (block) {
+                Slog.e(TAG, message + " blocked");
+                return false;
+            }
+            Slog.w(TAG, message + ", this should be avoided for performance reasons");
+            return true;
         }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index b42fe92..9e91875 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -77,7 +77,6 @@
     private final Map<String, AggregatedStats> mStats = new HashMap<>();
     private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>();
     private ArraySet<String> mStatExpiredkeys = new ArraySet<>();
-    private final SQLiteLog mSQLiteLog;
     private final Context mContext;
     private final Handler mHandler;
     private long mLastEmitTime;
@@ -85,7 +84,6 @@
     public NotificationUsageStats(Context context) {
         mContext = context;
         mLastEmitTime = SystemClock.elapsedRealtime();
-        mSQLiteLog = ENABLE_SQLITE_LOG ? new SQLiteLog(context) : null;
         mHandler = new Handler(mContext.getMainLooper()) {
             @Override
             public void handleMessage(Message msg) {
@@ -152,9 +150,6 @@
             stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logPosted(notification);
-        }
     }
 
     /**
@@ -170,9 +165,6 @@
             stats.countApiUse(notification);
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logPosted(notification);
-        }
     }
 
     /**
@@ -185,9 +177,6 @@
             stats.numRemovedByApp++;
         }
         releaseAggregatedStatsLocked(aggregatedStatsArray);
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logRemoved(notification);
-        }
     }
 
     /**
@@ -197,9 +186,6 @@
         MetricsLogger.histogram(mContext, "note_dismiss_longevity",
                 (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000));
         notification.stats.onDismiss();
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logDismissed(notification);
-        }
     }
 
     /**
@@ -209,9 +195,6 @@
         MetricsLogger.histogram(mContext, "note_click_longevity",
                 (int) (System.currentTimeMillis() - notification.getRankingTimeMs()) / (60 * 1000));
         notification.stats.onClick();
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.logClicked(notification);
-        }
     }
 
     public synchronized void registerPeopleAffinity(NotificationRecord notification, boolean valid,
@@ -328,21 +311,18 @@
                 // pass
             }
         }
-        if (ENABLE_SQLITE_LOG) {
-            try {
-                dump.put("historical", mSQLiteLog.dumpJson(filter));
-            } catch (JSONException e) {
-                // pass
-            }
-        }
         return dump;
     }
 
     public PulledStats remoteViewStats(long startMs, boolean aggregate) {
-        if (ENABLE_SQLITE_LOG) {
-            if (aggregate) {
-                return mSQLiteLog.remoteViewAggStats(startMs);
+        if (ENABLE_AGGREGATED_IN_MEMORY_STATS) {
+            PulledStats stats = new PulledStats(startMs);
+            for (AggregatedStats as : mStats.values()) {
+                if (as.numUndecoratedRemoteViews > 0) {
+                    stats.addUndecoratedPackage(as.key, as.mCreated);
+                }
             }
+            return stats;
         }
         return null;
     }
@@ -357,9 +337,6 @@
             pw.println(indent + "mStatsArrays.size(): " + mStatsArrays.size());
             pw.println(indent + "mStats.size(): " + mStats.size());
         }
-        if (ENABLE_SQLITE_LOG) {
-            mSQLiteLog.dump(pw, indent, filter);
-        }
     }
 
     public synchronized void emit() {
@@ -1046,353 +1023,4 @@
                     '}';
         }
     }
-
-    private static class SQLiteLog {
-        private static final String TAG = "NotificationSQLiteLog";
-
-        // Message types passed to the background handler.
-        private static final int MSG_POST = 1;
-        private static final int MSG_CLICK = 2;
-        private static final int MSG_REMOVE = 3;
-        private static final int MSG_DISMISS = 4;
-
-        private static final String DB_NAME = "notification_log.db";
-        private static final int DB_VERSION = 7;
-
-        /** Age in ms after which events are pruned from the DB. */
-        private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L;  // 1 week
-        /** Delay between pruning the DB. Used to throttle pruning. */
-        private static final long PRUNE_MIN_DELAY_MS = 6 * 60 * 60 * 1000L;  // 6 hours
-        /** Mininum number of writes between pruning the DB. Used to throttle pruning. */
-        private static final long PRUNE_MIN_WRITES = 1024;
-
-        // Table 'log'
-        private static final String TAB_LOG = "log";
-        private static final String COL_EVENT_USER_ID = "event_user_id";
-        private static final String COL_EVENT_TYPE = "event_type";
-        private static final String COL_EVENT_TIME = "event_time_ms";
-        private static final String COL_KEY = "key";
-        private static final String COL_PKG = "pkg";
-        private static final String COL_NOTIFICATION_ID = "nid";
-        private static final String COL_TAG = "tag";
-        private static final String COL_WHEN_MS = "when_ms";
-        private static final String COL_DEFAULTS = "defaults";
-        private static final String COL_FLAGS = "flags";
-        private static final String COL_IMPORTANCE_REQ = "importance_request";
-        private static final String COL_IMPORTANCE_FINAL = "importance_final";
-        private static final String COL_NOISY = "noisy";
-        private static final String COL_MUTED = "muted";
-        private static final String COL_DEMOTED = "demoted";
-        private static final String COL_CATEGORY = "category";
-        private static final String COL_ACTION_COUNT = "action_count";
-        private static final String COL_POSTTIME_MS = "posttime_ms";
-        private static final String COL_AIRTIME_MS = "airtime_ms";
-        private static final String COL_FIRST_EXPANSIONTIME_MS = "first_expansion_time_ms";
-        private static final String COL_AIRTIME_EXPANDED_MS = "expansion_airtime_ms";
-        private static final String COL_EXPAND_COUNT = "expansion_count";
-        private static final String COL_UNDECORATED = "undecorated";
-
-
-        private static final int EVENT_TYPE_POST = 1;
-        private static final int EVENT_TYPE_CLICK = 2;
-        private static final int EVENT_TYPE_REMOVE = 3;
-        private static final int EVENT_TYPE_DISMISS = 4;
-
-        private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
-
-        private static long sLastPruneMs;
-
-        private static long sNumWrites;
-        private final SQLiteOpenHelper mHelper;
-
-        private final Handler mWriteHandler;
-        private static final long DAY_MS = 24 * 60 * 60 * 1000;
-        private static final String STATS_QUERY = "SELECT " +
-                COL_EVENT_USER_ID + ", " +
-                COL_PKG + ", " +
-                // Bucket by day by looking at 'floor((midnight - eventTimeMs) / dayMs)'
-                "CAST(((%d - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
-                "AS day, " +
-                "COUNT(*) AS cnt, " +
-                "SUM(" + COL_MUTED + ") as muted, " +
-                "SUM(" + COL_NOISY + ") as noisy, " +
-                "SUM(" + COL_DEMOTED + ") as demoted, " +
-                "SUM(" + COL_UNDECORATED + ") as undecorated " +
-                "FROM " + TAB_LOG + " " +
-                "WHERE " +
-                COL_EVENT_TYPE + "=" + EVENT_TYPE_POST +
-                " AND " + COL_EVENT_TIME + " > %d " +
-                " GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
-        private static final String UNDECORATED_QUERY = "SELECT " +
-                COL_PKG + ", " +
-                "MAX(" + COL_EVENT_TIME + ") as max_time " +
-                "FROM " + TAB_LOG + " " +
-                "WHERE " + COL_UNDECORATED + "> 0 " +
-                " AND " + COL_EVENT_TIME + " > %d " +
-                "GROUP BY " + COL_PKG;
-
-        public SQLiteLog(Context context) {
-            HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
-                    android.os.Process.THREAD_PRIORITY_BACKGROUND);
-            backgroundThread.start();
-            mWriteHandler = new Handler(backgroundThread.getLooper()) {
-                @Override
-                public void handleMessage(Message msg) {
-                    NotificationRecord r = (NotificationRecord) msg.obj;
-                    long nowMs = System.currentTimeMillis();
-                    switch (msg.what) {
-                        case MSG_POST:
-                            writeEvent(r.getSbn().getPostTime(), EVENT_TYPE_POST, r);
-                            break;
-                        case MSG_CLICK:
-                            writeEvent(nowMs, EVENT_TYPE_CLICK, r);
-                            break;
-                        case MSG_REMOVE:
-                            writeEvent(nowMs, EVENT_TYPE_REMOVE, r);
-                            break;
-                        case MSG_DISMISS:
-                            writeEvent(nowMs, EVENT_TYPE_DISMISS, r);
-                            break;
-                        default:
-                            Log.wtf(TAG, "Unknown message type: " + msg.what);
-                            break;
-                    }
-                }
-            };
-            mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
-                @Override
-                public void onCreate(SQLiteDatabase db) {
-                    db.execSQL("CREATE TABLE " + TAB_LOG + " (" +
-                            "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
-                            COL_EVENT_USER_ID + " INT," +
-                            COL_EVENT_TYPE + " INT," +
-                            COL_EVENT_TIME + " INT," +
-                            COL_KEY + " TEXT," +
-                            COL_PKG + " TEXT," +
-                            COL_NOTIFICATION_ID + " INT," +
-                            COL_TAG + " TEXT," +
-                            COL_WHEN_MS + " INT," +
-                            COL_DEFAULTS + " INT," +
-                            COL_FLAGS + " INT," +
-                            COL_IMPORTANCE_REQ + " INT," +
-                            COL_IMPORTANCE_FINAL + " INT," +
-                            COL_NOISY + " INT," +
-                            COL_MUTED + " INT," +
-                            COL_DEMOTED + " INT," +
-                            COL_CATEGORY + " TEXT," +
-                            COL_ACTION_COUNT + " INT," +
-                            COL_POSTTIME_MS + " INT," +
-                            COL_AIRTIME_MS + " INT," +
-                            COL_FIRST_EXPANSIONTIME_MS + " INT," +
-                            COL_AIRTIME_EXPANDED_MS + " INT," +
-                            COL_EXPAND_COUNT + " INT," +
-                            COL_UNDECORATED + " INT" +
-                            ")");
-                }
-
-                @Override
-                public void onConfigure(SQLiteDatabase db) {
-                    // Memory optimization - close idle connections after 30s of inactivity
-                    setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
-                }
-
-                @Override
-                public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-                    if (oldVersion != newVersion) {
-                        db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
-                        onCreate(db);
-                    }
-                }
-            };
-        }
-
-        public void logPosted(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_POST, notification));
-        }
-
-        public void logClicked(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_CLICK, notification));
-        }
-
-        public void logRemoved(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_REMOVE, notification));
-        }
-
-        public void logDismissed(NotificationRecord notification) {
-            mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
-        }
-
-        private JSONArray jsonPostFrequencies(DumpFilter filter) throws JSONException {
-            JSONArray frequencies = new JSONArray();
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            long midnight = getMidnightMs();
-            String q = String.format(STATS_QUERY, midnight, filter.since);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    int userId = cursor.getInt(0);
-                    String pkg = cursor.getString(1);
-                    if (filter != null && !filter.matches(pkg)) continue;
-                    int day = cursor.getInt(2);
-                    int count = cursor.getInt(3);
-                    int muted = cursor.getInt(4);
-                    int noisy = cursor.getInt(5);
-                    int demoted = cursor.getInt(6);
-                    JSONObject row = new JSONObject();
-                    row.put("user_id", userId);
-                    row.put("package", pkg);
-                    row.put("day", day);
-                    row.put("count", count);
-                    row.put("noisy", noisy);
-                    row.put("muted", muted);
-                    row.put("demoted", demoted);
-                    frequencies.put(row);
-                }
-            } finally {
-                cursor.close();
-            }
-            return frequencies;
-        }
-
-        public void printPostFrequencies(PrintWriter pw, String indent, DumpFilter filter) {
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            long midnight = getMidnightMs();
-            String q = String.format(STATS_QUERY, midnight, filter.since);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    int userId = cursor.getInt(0);
-                    String pkg = cursor.getString(1);
-                    if (filter != null && !filter.matches(pkg)) continue;
-                    int day = cursor.getInt(2);
-                    int count = cursor.getInt(3);
-                    int muted = cursor.getInt(4);
-                    int noisy = cursor.getInt(5);
-                    int demoted = cursor.getInt(6);
-                    pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
-                            ",day=" + day + ",count=" + count + ",muted=" + muted + "/" + noisy +
-                            ",demoted=" + demoted + "}");
-                }
-            } finally {
-                cursor.close();
-            }
-        }
-
-        private long getMidnightMs() {
-            GregorianCalendar midnight = new GregorianCalendar();
-            midnight.set(midnight.get(Calendar.YEAR), midnight.get(Calendar.MONTH),
-                    midnight.get(Calendar.DATE), 23, 59, 59);
-            return midnight.getTimeInMillis();
-        }
-
-        private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r) {
-            ContentValues cv = new ContentValues();
-            cv.put(COL_EVENT_USER_ID, r.getSbn().getUser().getIdentifier());
-            cv.put(COL_EVENT_TIME, eventTimeMs);
-            cv.put(COL_EVENT_TYPE, eventType);
-            putNotificationIdentifiers(r, cv);
-            if (eventType == EVENT_TYPE_POST) {
-                putNotificationDetails(r, cv);
-            } else {
-                putPosttimeVisibility(r, cv);
-            }
-            cv.put(COL_UNDECORATED, (r.hasUndecoratedRemoteView() ? 1 : 0));
-            SQLiteDatabase db = mHelper.getWritableDatabase();
-            if (db.insert(TAB_LOG, null, cv) < 0) {
-                Log.wtf(TAG, "Error while trying to insert values: " + cv);
-            }
-            sNumWrites++;
-            pruneIfNecessary(db);
-        }
-
-        private void pruneIfNecessary(SQLiteDatabase db) {
-            // Prune if we haven't in a while.
-            long nowMs = System.currentTimeMillis();
-            if (sNumWrites > PRUNE_MIN_WRITES ||
-                    nowMs - sLastPruneMs > PRUNE_MIN_DELAY_MS) {
-                sNumWrites = 0;
-                sLastPruneMs = nowMs;
-                long horizonStartMs = nowMs - HORIZON_MS;
-                try {
-                    int deletedRows = db.delete(TAB_LOG, COL_EVENT_TIME + " < ?",
-                            new String[]{String.valueOf(horizonStartMs)});
-                    Log.d(TAG, "Pruned event entries: " + deletedRows);
-                } catch (SQLiteFullException e) {
-                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
-                }
-            }
-        }
-
-        private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_KEY, r.getSbn().getKey());
-            outCv.put(COL_PKG, r.getSbn().getPackageName());
-        }
-
-        private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_NOTIFICATION_ID, r.getSbn().getId());
-            if (r.getSbn().getTag() != null) {
-                outCv.put(COL_TAG, r.getSbn().getTag());
-            }
-            outCv.put(COL_WHEN_MS, r.getSbn().getPostTime());
-            outCv.put(COL_FLAGS, r.getNotification().flags);
-            final int before = r.stats.requestedImportance;
-            final int after = r.getImportance();
-            final boolean noisy = r.stats.isNoisy;
-            outCv.put(COL_IMPORTANCE_REQ, before);
-            outCv.put(COL_IMPORTANCE_FINAL, after);
-            outCv.put(COL_DEMOTED, after < before ? 1 : 0);
-            outCv.put(COL_NOISY, noisy);
-            if (noisy && after < IMPORTANCE_HIGH) {
-                outCv.put(COL_MUTED, 1);
-            } else {
-                outCv.put(COL_MUTED, 0);
-            }
-            if (r.getNotification().category != null) {
-                outCv.put(COL_CATEGORY, r.getNotification().category);
-            }
-            outCv.put(COL_ACTION_COUNT, r.getNotification().actions != null ?
-                    r.getNotification().actions.length : 0);
-        }
-
-        private static void putPosttimeVisibility(NotificationRecord r, ContentValues outCv) {
-            outCv.put(COL_POSTTIME_MS, r.stats.getCurrentPosttimeMs());
-            outCv.put(COL_AIRTIME_MS, r.stats.getCurrentAirtimeMs());
-            outCv.put(COL_EXPAND_COUNT, r.stats.userExpansionCount);
-            outCv.put(COL_AIRTIME_EXPANDED_MS, r.stats.getCurrentAirtimeExpandedMs());
-            outCv.put(COL_FIRST_EXPANSIONTIME_MS, r.stats.posttimeToFirstVisibleExpansionMs);
-        }
-
-        public void dump(PrintWriter pw, String indent, DumpFilter filter) {
-            printPostFrequencies(pw, indent, filter);
-        }
-
-        public JSONObject dumpJson(DumpFilter filter) {
-            JSONObject dump = new JSONObject();
-            try {
-                dump.put("post_frequency", jsonPostFrequencies(filter));
-                dump.put("since", filter.since);
-                dump.put("now", System.currentTimeMillis());
-            } catch (JSONException e) {
-                // pass
-            }
-            return dump;
-        }
-
-        public PulledStats remoteViewAggStats(long startMs) {
-            PulledStats stats = new PulledStats(startMs);
-            SQLiteDatabase db = mHelper.getReadableDatabase();
-            String q = String.format(UNDECORATED_QUERY, startMs);
-            Cursor cursor = db.rawQuery(q, null);
-            try {
-                for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
-                    String pkg = cursor.getString(0);
-                    long maxTimeMs = cursor.getLong(1);
-                    stats.addUndecoratedPackage(pkg, maxTimeMs);
-                }
-            } finally {
-                cursor.close();
-            }
-            return stats;
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 1104d4e..2e1543b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -746,9 +746,6 @@
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
         }
-        if (params.isStaged) {
-            mStagingManager.createSession(session);
-        }
 
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 37fa9a2e..649cafb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1602,13 +1602,6 @@
                         validateApkInstallLocked();
                     }
                 }
-            }
-
-            if (params.isStaged) {
-                mStagingManager.checkNonOverlappingWithStagedSessions(this);
-            }
-
-            synchronized (mLock) {
                 if (mDestroyed) {
                     throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                             "Session destroyed");
@@ -2311,7 +2304,7 @@
         return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
     }
 
-    private boolean sessionContains(Predicate<PackageInstallerSession> filter) {
+    boolean sessionContains(Predicate<PackageInstallerSession> filter) {
         if (!isMultiPackage()) {
             return filter.test(this);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9caa91b..805fbbc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17269,13 +17269,13 @@
         private final PackageSetting mPackageSetting;
         private final String mPackageName;
         private final String mPathString;
-        private final int mUserId;
+        private final int mUid;
         private final int[] mInstalledUserIds;
 
         IncrementalStatesCallback(PackageSetting packageSetting, int userId) {
             mPackageSetting = packageSetting;
             mPackageName = packageSetting.name;
-            mUserId = userId;
+            mUid = UserHandle.getUid(userId, packageSetting.appId);
             mPathString = packageSetting.getPathString();
             final int[] allUserIds = resolveUserIds(userId);
             final ArrayList<Integer> installedUserIds = new ArrayList<>();
@@ -17311,7 +17311,7 @@
                         ps, mInstalledUserIds, mSettings.mPackages);
             }
             Bundle extras = new Bundle();
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
             sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_LOADED, mPackageName,
                     extras, 0 /*flags*/,
@@ -17331,7 +17331,7 @@
                         ps, mInstalledUserIds, mSettings.mPackages);
             }
             Bundle extras = new Bundle();
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
             extras.putInt(Intent.EXTRA_REASON, reason);
             // send broadcast to users with this app installed
@@ -17353,7 +17353,7 @@
                         ps, mInstalledUserIds, mSettings.mPackages);
             }
             Bundle extras = new Bundle();
-            extras.putInt(Intent.EXTRA_UID, mUserId);
+            extras.putInt(Intent.EXTRA_UID, mUid);
             extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
             // send broadcast to users with this app installed
             sendPackageBroadcast(Intent.ACTION_PACKAGE_STARTABLE, mPackageName,
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 2db72784..3bad3cb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -709,6 +709,8 @@
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
+        // Store this parent session which will be used to check overlapping later
+        createSession(session);
         mPreRebootVerificationHandler.startPreRebootVerification(session);
     }
 
@@ -738,14 +740,16 @@
      * </ul>
      * @throws PackageManagerException if session fails the check
      */
-    void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
+    private void checkNonOverlappingWithStagedSessions(@NonNull PackageInstallerSession session)
             throws PackageManagerException {
         if (session.isMultiPackage()) {
             // We cannot say a parent session overlaps until we process its children
             return;
         }
-        if (session.getPackageName() == null) {
-            throw new PackageManagerException(PackageManager.INSTALL_FAILED_INVALID_APK,
+
+        String packageName = session.getPackageName();
+        if (packageName == null) {
+            throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Cannot stage session " + session.sessionId + " with package name null");
         }
 
@@ -757,40 +761,26 @@
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
-                if (!stagedSession.isCommitted() || stagedSession.isStagedAndInTerminalState()
+                if (stagedSession.hasParentSessionId() || !stagedSession.isCommitted()
+                        || stagedSession.isStagedAndInTerminalState()
                         || stagedSession.isDestroyed()) {
                     continue;
                 }
-                if (stagedSession.isMultiPackage()) {
-                    // This active parent staged session is useless as it doesn't have a package
-                    // name and the session we are checking is not a parent session either.
-                    continue;
-                }
-                // Check if stagedSession has an active parent session or not
-                if (stagedSession.hasParentSessionId()) {
-                    final int parentId = stagedSession.getParentSessionId();
-                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
-                    if (parentSession == null || parentSession.isStagedAndInTerminalState()
-                            || parentSession.isDestroyed()) {
-                        // Parent session has been abandoned or terminated already
-                        continue;
-                    }
-                }
 
-                // From here on, stagedSession is a non-parent active staged session
+                // From here on, stagedSession is a parent active staged session
 
                 // Check if session is one of the active sessions
-                if (session.sessionId == stagedSession.sessionId) {
+                if (getSessionIdForParentOrSelf(session) == stagedSession.sessionId) {
                     Slog.w(TAG, "Session " + session.sessionId + " is already staged");
                     continue;
                 }
 
                 // New session cannot have same package name as one of the active sessions
-                if (session.getPackageName().equals(stagedSession.getPackageName())) {
+                if (stagedSession.sessionContains(s -> s.getPackageName().equals(packageName))) {
                     if (isRollback) {
                         // If the new session is a rollback, then it gets priority. The existing
                         // session is failed to unblock rollback.
-                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+                        final PackageInstallerSession root = stagedSession;
                         if (!ensureActiveApexSessionIsAborted(root)) {
                             Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
                             // Safe to ignore active apex session abort failure since session
@@ -804,7 +794,7 @@
                                 + "blocking rollback session: " + session.sessionId);
                     } else {
                         throw new PackageManagerException(
-                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                                 "Package: " + session.getPackageName() + " in session: "
                                         + session.sessionId + " has been staged already by session:"
                                         + " " + stagedSession.sessionId, null);
@@ -814,17 +804,16 @@
                 // Staging multiple root sessions is not allowed if device doesn't support
                 // checkpoint. If session and stagedSession do not have common ancestor, they are
                 // from two different root sessions.
-                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
-                        != getSessionIdForParentOrSelf(stagedSession)) {
+                if (!supportsCheckpoint) {
                     throw new PackageManagerException(
-                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                            SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                             "Cannot stage multiple sessions without checkpoint support", null);
                 }
             }
         }
     }
 
-    void createSession(@NonNull PackageInstallerSession sessionInfo) {
+    private void createSession(@NonNull PackageInstallerSession sessionInfo) {
         synchronized (mStagedSessions) {
             mStagedSessions.append(sessionInfo.sessionId, sessionInfo);
         }
@@ -900,16 +889,15 @@
     }
 
     void restoreSession(@NonNull PackageInstallerSession session, boolean isDeviceUpgrading) {
-        PackageInstallerSession sessionToResume = session;
-        synchronized (mStagedSessions) {
-            mStagedSessions.append(session.sessionId, session);
-            if (session.hasParentSessionId()) {
-                // Only parent sessions can be restored
-                return;
-            }
+        if (session.hasParentSessionId()) {
+            // Only parent sessions can be restored
+            return;
         }
+        // Store this parent session which will be used to check overlapping later
+        createSession(session);
         // The preconditions used during pre-reboot verification might have changed when device
         // is upgrading. Updated staged sessions to activation failed before we resume the session.
+        PackageInstallerSession sessionToResume = session;
         if (isDeviceUpgrading && !sessionToResume.isStagedAndInTerminalState()) {
             sessionToResume.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "Build fingerprint has changed");
@@ -1160,6 +1148,19 @@
          * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
          */
         private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
+            try {
+                if (session.isMultiPackage()) {
+                    for (PackageInstallerSession s : session.getChildSessions()) {
+                        checkNonOverlappingWithStagedSessions(s);
+                    }
+                } else {
+                    checkNonOverlappingWithStagedSessions(session);
+                }
+            } catch (PackageManagerException e) {
+                onPreRebootVerificationFailure(session, e.error, e.getMessage());
+                return;
+            }
+
             int rollbackId = -1;
             if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                 // If rollback is enabled for this session, we call through to the RollbackManager
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2ada613..3ec156f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4708,9 +4708,12 @@
                 final UserInfo user = users.get(i);
                 final boolean running = am.isUserRunning(user.id, 0);
                 final boolean current = user.id == currentUser;
+                final boolean hasParent = user.profileGroupId != user.id
+                        && user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID;
                 if (verbose) {
-                    pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s\n", i, user.id, user.name,
+                    pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s%s\n", i, user.id, user.name,
                             UserInfo.flagsToString(user.flags),
+                            hasParent ? " (parentId=" + user.profileGroupId + ")" : "",
                             running ? " (running)" : "",
                             user.partial ? " (partial)" : "",
                             user.preCreated ? " (pre-created)" : "",
@@ -4791,6 +4794,11 @@
                     pw.print("  "); pw.print(userInfo);
                     pw.print(" serialNo="); pw.print(userInfo.serialNumber);
                     pw.print(" isPrimary="); pw.print(userInfo.isPrimary());
+                    if (userInfo.profileGroupId != userInfo.id
+                            &&  userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
+                        pw.print(" parentId="); pw.print(userInfo.profileGroupId);
+                    }
+
                     if (mRemovingUserIds.get(userId)) {
                         pw.print(" <removing> ");
                     }
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index eb06bf9..c086017 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -433,18 +433,6 @@
         throw new SecurityException("No permission tree found for " + permName);
     }
 
-    public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
-            UidPermissionState uidState) {
-        if (!uidState.hasPermissionState(name) && !pkg.getRequestedPermissions().contains(name)) {
-            throw new SecurityException("Package " + pkg.getPackageName()
-                    + " has not requested permission " + name);
-        }
-        if (!isRuntime() && !isDevelopment()) {
-            throw new SecurityException("Permission " + name + " requested by "
-                    + pkg.getPackageName() + " is not a changeable permission type");
-        }
-    }
-
     private static BasePermission findPermissionTree(
             Collection<BasePermission> permissionTrees, String permName) {
         for (BasePermission bp : permissionTrees) {
diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
index b9456acf..18936dd 100644
--- a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java
@@ -21,57 +21,38 @@
 import android.annotation.UserIdInt;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 /**
  * Permission state for this device.
  */
 public final class DevicePermissionState {
-    @GuardedBy("mLock")
-    @NonNull
     private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();
 
-    @NonNull
-    private final Object mLock;
-
-    public DevicePermissionState(@NonNull Object lock) {
-        mLock = lock;
-    }
-
     @Nullable
     public UserPermissionState getUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            return mUserStates.get(userId);
-        }
+        return mUserStates.get(userId);
     }
 
     @NonNull
     public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            UserPermissionState userState = mUserStates.get(userId);
-            if (userState == null) {
-                userState = new UserPermissionState(mLock);
-                mUserStates.put(userId, userState);
-            }
-            return userState;
+        UserPermissionState userState = mUserStates.get(userId);
+        if (userState == null) {
+            userState = new UserPermissionState();
+            mUserStates.put(userId, userState);
         }
+        return userState;
     }
 
     public void removeUserState(@UserIdInt int userId) {
-        synchronized (mLock) {
-            mUserStates.delete(userId);
-        }
+        mUserStates.delete(userId);
     }
 
     public int[] getUserIds() {
-        synchronized (mLock) {
-            final int userStatesSize = mUserStates.size();
-            final int[] userIds = new int[userStatesSize];
-            for (int i = 0; i < userStatesSize; i++) {
-                final int userId = mUserStates.keyAt(i);
-                userIds[i] = userId;
-            }
-            return userIds;
+        final int userStatesSize = mUserStates.size();
+        final int[] userIds = new int[userStatesSize];
+        for (int i = 0; i < userStatesSize; i++) {
+            final int userId = mUserStates.keyAt(i);
+            userIds[i] = userId;
         }
+        return userIds;
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d356565..25e1848 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -216,8 +216,9 @@
     /** Internal connection to the user manager */
     private final UserManagerInternal mUserManagerInt;
 
+    @GuardedBy("mLock")
     @NonNull
-    private final DevicePermissionState mState;
+    private final DevicePermissionState mState = new DevicePermissionState();
 
     /** Permission controller: User space permission management */
     private PermissionControllerManager mPermissionControllerManager;
@@ -386,7 +387,6 @@
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
         mSettings = new PermissionSettings(mLock);
-        mState = new DevicePermissionState(mLock);
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
         mHandlerThread = new ServiceThread(TAG,
@@ -665,20 +665,23 @@
         if (pkg == null) {
             return 0;
         }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            return 0;
+        }
+
         synchronized (mLock) {
             if (mSettings.getPermissionLocked(permName) == null) {
                 return 0;
             }
+
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return 0;
+            }
+
+            return uidState.getPermissionFlags(permName);
         }
-        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
-            return 0;
-        }
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return 0;
-        }
-        return uidState.getPermissionFlags(permName);
     }
 
     @Override
@@ -772,52 +775,52 @@
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
 
-        final BasePermission bp;
-        synchronized (mLock) {
-            bp = mSettings.getPermissionLocked(permName);
+        boolean isRequested = false;
+        // Fast path, the current package has requested the permission.
+        if (pkg.getRequestedPermissions().contains(permName)) {
+            isRequested = true;
         }
-        if (bp == null) {
-            throw new IllegalArgumentException("Unknown permission: " + permName);
-        }
-
-        if (bp.isInstallerExemptIgnored()) {
-            flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-        }
-
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return;
-        }
-
-        final boolean hadState = uidState.getPermissionState(permName) != null;
-        if (!hadState) {
-            boolean isRequested = false;
-            // Fast path, the current package has requested the permission.
-            if (pkg.getRequestedPermissions().contains(permName)) {
-                isRequested = true;
-            }
-            if (!isRequested) {
-                // Slow path, go through all shared user packages.
-                String[] sharedUserPackageNames =
-                        mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId);
-                for (String sharedUserPackageName : sharedUserPackageNames) {
-                    AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage(
-                            sharedUserPackageName);
-                    if (sharedUserPkg != null
-                            && sharedUserPkg.getRequestedPermissions().contains(permName)) {
-                        isRequested = true;
-                        break;
-                    }
+        if (!isRequested) {
+            // Slow path, go through all shared user packages.
+            String[] sharedUserPackageNames =
+                    mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId);
+            for (String sharedUserPackageName : sharedUserPackageNames) {
+                AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage(
+                        sharedUserPackageName);
+                if (sharedUserPkg != null
+                        && sharedUserPkg.getRequestedPermissions().contains(permName)) {
+                    isRequested = true;
+                    break;
                 }
             }
-            if (!isRequested) {
+        }
+
+        final BasePermission bp;
+        final boolean permissionUpdated;
+        synchronized (mLock) {
+            bp = mSettings.getPermissionLocked(permName);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + permName);
+            }
+
+            if (bp.isInstallerExemptIgnored()) {
+                flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+            }
+
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return;
+            }
+
+            if (!uidState.hasPermissionState(permName) && !isRequested) {
                 Log.e(TAG, "Permission " + permName + " isn't requested by package " + packageName);
                 return;
             }
+
+            permissionUpdated = uidState.updatePermissionFlags(bp, flagMask, flagValues);
         }
-        final boolean permissionUpdated =
-                uidState.updatePermissionFlags(bp, flagMask, flagValues);
+
         if (permissionUpdated && bp.isRuntime()) {
             notifyRuntimePermissionStateChanged(packageName, userId);
         }
@@ -861,14 +864,17 @@
 
         final boolean[] changed = new boolean[1];
         mPackageManagerInt.forEachPackage(pkg -> {
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                        + userId);
-                return;
+            synchronized (mLock) {
+                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG,
+                            "Missing permissions state for " + pkg.getPackageName() + " and user "
+                                    + userId);
+                    return;
+                }
+                changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
+                        effectiveFlagMask, effectiveFlagValues);
             }
-            changed[0] |= uidState.updatePermissionFlagsForAllPermissions(
-                    effectiveFlagMask, effectiveFlagValues);
             mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid());
         });
 
@@ -920,33 +926,37 @@
         }
 
         final int uid = UserHandle.getUid(userId, pkg.getUid());
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return PackageManager.PERMISSION_DENIED;
-        }
+        final boolean isInstantApp = mPackageManagerInt.getInstantAppPackageName(uid) != null;
 
-        if (checkSinglePermissionInternal(uid, uidState, permissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return PackageManager.PERMISSION_DENIED;
+            }
 
-        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
-        if (fullerPermissionName != null
-                && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
+            if (checkSinglePermissionInternalLocked(uidState, permissionName, isInstantApp)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+
+            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+            if (fullerPermissionName != null && checkSinglePermissionInternalLocked(uidState,
+                    fullerPermissionName, isInstantApp)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
         }
 
         return PackageManager.PERMISSION_DENIED;
     }
 
-    private boolean checkSinglePermissionInternal(int uid,
-            @NonNull UidPermissionState uidState, @NonNull String permissionName) {
+    private boolean checkSinglePermissionInternalLocked(@NonNull UidPermissionState uidState,
+            @NonNull String permissionName, boolean isInstantApp) {
         if (!uidState.isPermissionGranted(permissionName)) {
             return false;
         }
 
-        if (mPackageManagerInt.getInstantAppPackageName(uid) != null) {
+        if (isInstantApp) {
             return mSettings.isPermissionInstant(permissionName);
         }
 
@@ -994,24 +1004,25 @@
             return checkPermissionInternal(pkg, false, permissionName, userId);
         }
 
-        if (checkSingleUidPermissionInternal(uid, permissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
-        }
+        synchronized (mLock) {
+            if (checkSingleUidPermissionInternalLocked(uid, permissionName)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
 
-        final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
-        if (fullerPermissionName != null
-                && checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
-            return PackageManager.PERMISSION_GRANTED;
+            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
+            if (fullerPermissionName != null
+                    && checkSingleUidPermissionInternalLocked(uid, fullerPermissionName)) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
         }
 
         return PackageManager.PERMISSION_DENIED;
     }
 
-    private boolean checkSingleUidPermissionInternal(int uid, @NonNull String permissionName) {
-        synchronized (mLock) {
-            ArraySet<String> permissions = mSystemPermissions.get(uid);
-            return permissions != null && permissions.contains(permissionName);
-        }
+    private boolean checkSingleUidPermissionInternalLocked(int uid,
+            @NonNull String permissionName) {
+        ArraySet<String> permissions = mSystemPermissions.get(uid);
+        return permissions != null && permissions.contains(permissionName);
     }
 
     @Override
@@ -1137,42 +1148,45 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-                return null;
-            }
-
-            int queryFlags = 0;
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
-                queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
-                queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
-                queryFlags |=  FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-            }
-            if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
-                queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-            }
-
-            ArrayList<String> whitelistedPermissions = null;
-
-            final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
-            for (int i = 0; i < permissionCount; i++) {
-                final String permissionName = pkg.getRequestedPermissions().get(i);
-                final int currentFlags =
-                        uidState.getPermissionFlags(permissionName);
-                if ((currentFlags & queryFlags) != 0) {
-                    if (whitelistedPermissions == null) {
-                        whitelistedPermissions = new ArrayList<>();
-                    }
-                    whitelistedPermissions.add(permissionName);
+            synchronized (mLock) {
+                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for " + packageName + " and user "
+                            + userId);
+                    return null;
                 }
-            }
 
-            return whitelistedPermissions;
+                int queryFlags = 0;
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
+                    queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+                }
+                if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
+                    queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                }
+
+                ArrayList<String> whitelistedPermissions = null;
+
+                final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
+                for (int i = 0; i < permissionCount; i++) {
+                    final String permissionName = pkg.getRequestedPermissions().get(i);
+                    final int currentFlags =
+                            uidState.getPermissionFlags(permissionName);
+                    if ((currentFlags & queryFlags) != 0) {
+                        if (whitelistedPermissions == null) {
+                            whitelistedPermissions = new ArrayList<>();
+                        }
+                        whitelistedPermissions.add(permissionName);
+                    }
+                }
+
+                return whitelistedPermissions;
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1437,12 +1451,15 @@
                 "grantRuntimePermission");
 
         final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName);
-        final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
-                packageName);
+        final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
         if (pkg == null || ps == null) {
             Log.e(TAG, "Unknown package: " + packageName);
             return;
         }
+        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
+            throw new IllegalArgumentException("Unknown package: " + packageName);
+        }
+
         final BasePermission bp;
         synchronized (mLock) {
             bp = mSettings.getPermissionLocked(permName);
@@ -1450,46 +1467,17 @@
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
-        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
 
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
+        if (!(bp.isRuntime() || bp.isDevelopment())) {
+            throw new SecurityException("Permission " + permName + " requested by "
+                    + pkg.getPackageName() + " is not a changeable permission type");
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
-
         // If a permission review is required for legacy apps we represent
         // their permissions as always granted runtime ones since we need
         // to keep the review required permission flag per user while an
         // install permission's state is shared across all users.
-        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
-                && bp.isRuntime()) {
-            return;
-        }
-
-        final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
-
-        final int flags = uidState.getPermissionFlags(permName);
-        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
-            Log.e(TAG, "Cannot grant system fixed permission "
-                    + permName + " for package " + packageName);
-            return;
-        }
-        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-            Log.e(TAG, "Cannot grant policy fixed permission "
-                    + permName + " for package " + packageName);
-            return;
-        }
-
-        if (bp.isHardRestricted()
-                && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
-            Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
-                    + permName + " for package " + packageName);
+        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) {
             return;
         }
 
@@ -1501,36 +1489,61 @@
             return;
         }
 
-        if (bp.isDevelopment()) {
-            // Development permissions must be handled specially, since they are not
-            // normal runtime permissions.  For now they apply to all users.
-            // TODO(zhanghai): We are breaking the behavior above by making all permission state
-            //  per-user. It isn't documented behavior and relatively rarely used anyway.
-            if (uidState.grantPermission(bp)) {
-                if (callback != null) {
-                    callback.onInstallPermissionGranted();
-                }
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return;
             }
-            return;
-        }
 
-        if (ps.getInstantApp(userId) && !bp.isInstant()) {
-            throw new SecurityException("Cannot grant non-ephemeral permission"
-                    + permName + " for package " + packageName);
-        }
+            if (!(uidState.hasPermissionState(permName)
+                    || pkg.getRequestedPermissions().contains(permName))) {
+                throw new SecurityException("Package " + pkg.getPackageName()
+                        + " has not requested permission " + permName);
+            }
 
-        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
-            Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
-            return;
-        }
+            final int flags = uidState.getPermissionFlags(permName);
+            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
+                Log.e(TAG, "Cannot grant system fixed permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
+            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                Log.e(TAG, "Cannot grant policy fixed permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
 
-        if (!uidState.grantPermission(bp)) {
-            return;
-        }
+            if (bp.isHardRestricted()
+                    && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
+                Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
+                        + permName + " for package " + packageName);
+                return;
+            }
 
-        if (bp.hasGids()) {
-            if (callback != null) {
-                callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
+            if (bp.isDevelopment()) {
+                // Development permissions must be handled specially, since they are not
+                // normal runtime permissions.  For now they apply to all users.
+                // TODO(zhanghai): We are breaking the behavior above by making all permission state
+                //  per-user. It isn't documented behavior and relatively rarely used anyway.
+                if (!uidState.grantPermission(bp)) {
+                    return;
+                }
+            } else {
+                if (ps.getInstantApp(userId) && !bp.isInstant()) {
+                    throw new SecurityException("Cannot grant non-ephemeral permission" + permName
+                            + " for package " + packageName);
+                }
+
+                if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+                    Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
+                    return;
+                }
+
+                if (!uidState.grantPermission(bp)) {
+                    return;
+                }
             }
         }
 
@@ -1538,8 +1551,16 @@
             logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);
         }
 
+        final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
         if (callback != null) {
-            callback.onPermissionGranted(uid, userId);
+            if (bp.isDevelopment()) {
+                callback.onInstallPermissionGranted();
+            } else {
+                callback.onPermissionGranted(uid, userId);
+            }
+            if (bp.hasGids()) {
+                callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
+            }
         }
 
         if (bp.isRuntime()) {
@@ -1593,56 +1614,57 @@
         if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
-        final BasePermission bp = mSettings.getPermissionLocked(permName);
+        final BasePermission bp = mSettings.getPermission(permName);
         if (bp == null) {
             throw new IllegalArgumentException("Unknown permission: " + permName);
         }
 
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
+        if (!(bp.isRuntime() || bp.isDevelopment())) {
+            throw new SecurityException("Permission " + permName + " requested by "
+                    + pkg.getPackageName() + " is not a changeable permission type");
         }
 
-        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState);
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return;
+            }
 
-        // If a permission review is required for legacy apps we represent
-        // their permissions as always granted runtime ones since we need
-        // to keep the review required permission flag per user while an
-        // install permission's state is shared across all users.
-        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
-                && bp.isRuntime()) {
-            return;
-        }
+            if (!(uidState.hasPermissionState(permName)
+                    || pkg.getRequestedPermissions().contains(permName))) {
+                throw new SecurityException("Package " + pkg.getPackageName()
+                        + " has not requested permission " + permName);
+            }
 
-        final int flags = uidState.getPermissionFlags(permName);
-        // Only the system may revoke SYSTEM_FIXED permissions.
-        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
-                && UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
-            throw new SecurityException("Non-System UID cannot revoke system fixed permission "
-                    + permName + " for package " + packageName);
-        }
-        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-            throw new SecurityException("Cannot revoke policy fixed permission "
-                    + permName + " for package " + packageName);
-        }
+            // If a permission review is required for legacy apps we represent
+            // their permissions as always granted runtime ones since we need
+            // to keep the review required permission flag per user while an
+            // install permission's state is shared across all users.
+            if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) {
+                return;
+            }
 
-        if (bp.isDevelopment()) {
+            final int flags = uidState.getPermissionFlags(permName);
+            // Only the system may revoke SYSTEM_FIXED permissions.
+            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
+                    && UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
+                throw new SecurityException("Non-System UID cannot revoke system fixed permission "
+                        + permName + " for package " + packageName);
+            }
+            if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                throw new SecurityException("Cannot revoke policy fixed permission "
+                        + permName + " for package " + packageName);
+            }
+
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             // TODO(zhanghai): We are breaking the behavior above by making all permission state
             //  per-user. It isn't documented behavior and relatively rarely used anyway.
-            if (uidState.revokePermission(bp)) {
-                if (callback != null) {
-                    mDefaultPermissionCallback.onInstallPermissionRevoked();
-                }
+            if (!uidState.revokePermission(bp)) {
+                return;
             }
-            return;
-        }
-
-        if (!uidState.revokePermission(bp)) {
-            return;
         }
 
         if (bp.isRuntime()) {
@@ -1650,8 +1672,12 @@
         }
 
         if (callback != null) {
-            callback.onPermissionRevoked(UserHandle.getUid(userId,
-                    UserHandle.getAppId(pkg.getUid())), userId, reason);
+            if (bp.isDevelopment()) {
+                mDefaultPermissionCallback.onInstallPermissionRevoked();
+            } else {
+                callback.onPermissionRevoked(UserHandle.getUid(userId,
+                        UserHandle.getAppId(pkg.getUid())), userId, reason);
+            }
         }
 
         if (bp.isRuntime()) {
@@ -1686,7 +1712,6 @@
      * @param pkg The package for which to reset.
      * @param userId The device user for which to do a reset.
      */
-    @GuardedBy("mLock")
     private void resetRuntimePermissionsInternal(final AndroidPackage pkg,
             final int userId) {
         final String packageName = pkg.getPackageName();
@@ -2323,7 +2348,7 @@
             // Assume by default that we did not install this permission into the system.
             p.setFlags(p.getFlags() & ~PermissionInfo.FLAG_INSTALLED);
 
-            synchronized (PermissionManagerService.this.mLock) {
+            synchronized (mLock) {
                 // Now that permission groups have a special meaning, we ignore permission
                 // groups for legacy apps to prevent unexpected behavior. In particular,
                 // permissions for one app being granted to someone just because they happen
@@ -2463,31 +2488,35 @@
         if (ps == null) {
             return Collections.emptySet();
         }
-        final UidPermissionState uidState = getUidState(ps, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
-            return Collections.emptySet();
-        }
-        if (!ps.getInstantApp(userId)) {
-            return uidState.getGrantedPermissions();
-        } else {
-            // Install permission state is shared among all users, but instant app state is
-            // per-user, so we can only filter it here unless we make install permission state
-            // per-user as well.
-            final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions());
-            instantPermissions.removeIf(permissionName -> {
-                BasePermission permission = mSettings.getPermission(permissionName);
-                if (permission == null) {
-                    return true;
-                }
-                if (!permission.isInstant()) {
-                    EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
-                            ps.getAppId()), permissionName);
-                    return true;
-                }
-                return false;
-            });
-            return instantPermissions;
+
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(ps, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
+                return Collections.emptySet();
+            }
+            if (!ps.getInstantApp(userId)) {
+                return uidState.getGrantedPermissions();
+            } else {
+                // Install permission state is shared among all users, but instant app state is
+                // per-user, so we can only filter it here unless we make install permission state
+                // per-user as well.
+                final Set<String> instantPermissions =
+                        new ArraySet<>(uidState.getGrantedPermissions());
+                instantPermissions.removeIf(permissionName -> {
+                    BasePermission permission = mSettings.getPermission(permissionName);
+                    if (permission == null) {
+                        return true;
+                    }
+                    if (!permission.isInstant()) {
+                        EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId,
+                                ps.getAppId()), permissionName);
+                        return true;
+                    }
+                    return false;
+                });
+                return instantPermissions;
+            }
         }
     }
 
@@ -3546,13 +3575,15 @@
         }
 
         // Legacy apps have the permission and get user consent on launch.
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return false;
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return false;
+            }
+            return uidState.isPermissionReviewRequired();
         }
-        return uidState.isPermissionReviewRequired();
     }
 
     private void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
@@ -3565,13 +3596,6 @@
 
     private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId,
             String[] grantedPermissions, int callingUid, PermissionCallback callback) {
-        final UidPermissionState uidState = getUidState(pkg, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                    + userId);
-            return;
-        }
-
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 
@@ -3593,7 +3617,8 @@
                     && (supportsRuntimePermissions || !bp.isRuntimeOnly())
                     && (grantedPermissions == null
                            || ArrayUtils.contains(grantedPermissions, permission))) {
-                final int flags = uidState.getPermissionFlags(permission);
+                final int flags = getPermissionFlagsInternal(permission, pkg.getPackageName(),
+                        callingUid, userId);
                 if (supportsRuntimePermissions) {
                     // Installer cannot change immutable permissions.
                     if ((flags & immutableFlags) == 0) {
@@ -3621,12 +3646,6 @@
 
         for (int i = 0; i < userIds.length; i++) {
             int userId = userIds[i];
-            final UidPermissionState uidState = getUidState(pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
-                        + userId);
-                continue;
-            }
 
             for (int j = 0; j < permissionCount; j++) {
                 final String permissionName = pkg.getRequestedPermissions().get(j);
@@ -3637,14 +3656,26 @@
                     continue;
                 }
 
-                if (uidState.isPermissionGranted(permissionName)) {
+                final boolean isGranted;
+                synchronized (mLock) {
+                    final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                    if (uidState == null) {
+                        Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+                                + " and user " + userId);
+                        continue;
+                    }
+                    isGranted = uidState.isPermissionGranted(permissionName);
+                }
+
+                if (isGranted) {
                     if (oldGrantedRestrictedPermissions.get(userId) == null) {
                         oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
                     }
                     oldGrantedRestrictedPermissions.get(userId).add(permissionName);
                 }
 
-                final int oldFlags = uidState.getPermissionFlags(permissionName);
+                final int oldFlags = getPermissionFlagsInternal(permissionName,
+                        pkg.getPackageName(), callingUid, userId);
 
                 int newFlags = oldFlags;
                 int mask = 0;
@@ -3708,7 +3739,6 @@
                 // as whitelisting trumps policy i.e. policy cannot grant a non
                 // grantable permission.
                 if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-                    final boolean isGranted = uidState.isPermissionGranted(permissionName);
                     if (!isWhitelisted && isGranted) {
                         mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
                         newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3742,15 +3772,19 @@
 
                 final int oldGrantedCount = oldPermsForUser.size();
                 for (int j = 0; j < oldGrantedCount; j++) {
-                    final String permission = oldPermsForUser.valueAt(j);
+                    final String permissionName = oldPermsForUser.valueAt(j);
                     // Sometimes we create a new permission state instance during update.
-                    final UidPermissionState newUidState = getUidState(pkg, userId);
-                    if (newUidState == null) {
-                        Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
-                                + " and user " + userId);
-                        continue;
+                    final boolean isGranted;
+                    synchronized (mLock) {
+                        final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                        if (uidState == null) {
+                            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+                                    + " and user " + userId);
+                            continue;
+                        }
+                        isGranted = uidState.isPermissionGranted(permissionName);
                     }
-                    if (!newUidState.isPermissionGranted(permission)) {
+                    if (!isGranted) {
                         callback.onPermissionRevoked(pkg.getUid(), userId, null);
                         break;
                     }
@@ -3799,13 +3833,6 @@
                 continue;
             }
 
-            UidPermissionState uidState = getUidState(deletedPs.pkg, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
-                        + " and user " + userId);
-                continue;
-            }
-
             PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
                     deletedPs.pkg.getPackageName());
 
@@ -3824,10 +3851,19 @@
                 }
             }
 
-            // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
-            //  permission change?
-            if (uidState.removePermissionState(bp.name) && bp.hasGids()) {
-                affectedUserId = userId;
+            synchronized (mLock) {
+                UidPermissionState uidState = getUidStateLocked(deletedPs.pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()
+                            + " and user " + userId);
+                    continue;
+                }
+
+                // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any
+                //  permission change?
+                if (uidState.removePermissionState(bp.name) && bp.hasGids()) {
+                    affectedUserId = userId;
+                }
             }
         }
 
@@ -4122,14 +4158,17 @@
                     } else {
                         mPackageManagerInt.forEachPackage(p -> {
                             final int[] userIds = mUserManagerInt.getUserIds();
-                            for (final int userId : userIds) {
-                                final UidPermissionState uidState = getUidState(p, userId);
-                                if (uidState == null) {
-                                    Slog.e(TAG, "Missing permissions state for "
-                                            + p.getPackageName() + " and user " + userId);
-                                    return;
+                            synchronized (mLock) {
+                                for (final int userId : userIds) {
+                                    final UidPermissionState uidState = getUidStateLocked(p,
+                                            userId);
+                                    if (uidState == null) {
+                                        Slog.e(TAG, "Missing permissions state for "
+                                                + p.getPackageName() + " and user " + userId);
+                                        continue;
+                                    }
+                                    uidState.removePermissionState(bp.name);
                                 }
-                                uidState.removePermissionState(bp.name);
                             }
                         });
                     }
@@ -4537,26 +4576,24 @@
     }
 
     @Nullable
-    private UidPermissionState getUidState(@NonNull PackageSetting ps,
+    private UidPermissionState getUidStateLocked(@NonNull PackageSetting ps,
             @UserIdInt int userId) {
-        return getUidState(ps.getAppId(), userId);
+        return getUidStateLocked(ps.getAppId(), userId);
     }
 
     @Nullable
-    private UidPermissionState getUidState(@NonNull AndroidPackage pkg,
+    private UidPermissionState getUidStateLocked(@NonNull AndroidPackage pkg,
             @UserIdInt int userId) {
-        return getUidState(pkg.getUid(), userId);
+        return getUidStateLocked(pkg.getUid(), userId);
     }
 
     @Nullable
-    private UidPermissionState getUidState(int appId, @UserIdInt int userId) {
-        synchronized (mLock) {
-            final UserPermissionState userState = mState.getUserState(userId);
-            if (userState == null) {
-                return null;
-            }
-            return userState.getUidState(appId);
+    private UidPermissionState getUidStateLocked(@AppIdInt int appId, @UserIdInt int userId) {
+        final UserPermissionState userState = mState.getUserState(userId);
+        if (userState == null) {
+            return null;
         }
+        return userState.getUidState(appId);
     }
 
     private void removeAppIdState(@AppIdInt int appId) {
@@ -4601,7 +4638,10 @@
     }
 
     private void writeStateToPackageSettings() {
-        final int[] userIds = mState.getUserIds();
+        final int[] userIds;
+        synchronized (mLock) {
+            userIds = mState.getUserIds();
+        }
         mPackageManagerInt.forEachPackageSetting(ps -> {
             ps.setInstallPermissionsFixed(false);
             final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
@@ -4652,27 +4692,30 @@
     @NonNull
     private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
         final LegacyPermissionState legacyState = new LegacyPermissionState();
-        final int[] userIds = mState.getUserIds();
-        for (final int userId : userIds) {
-            final UidPermissionState uidState = getUidState(appId, userId);
-            if (uidState == null) {
-                Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
-                        + userId);
-                continue;
-            }
+        synchronized (mLock) {
+            final int[] userIds = mState.getUserIds();
+            for (final int userId : userIds) {
+                final UidPermissionState uidState = getUidStateLocked(appId, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+                            + userId);
+                    continue;
+                }
 
-            final List<PermissionState> permissionStates = uidState.getPermissionStates();
-            final int permissionStatesSize = permissionStates.size();
-            for (int i = 0; i < permissionStatesSize; i++) {
-                final PermissionState permissionState = permissionStates.get(i);
+                final List<PermissionState> permissionStates = uidState.getPermissionStates();
+                final int permissionStatesSize = permissionStates.size();
+                for (int i = 0; i < permissionStatesSize; i++) {
+                    final PermissionState permissionState = permissionStates.get(i);
 
-                final LegacyPermissionState.PermissionState legacyPermissionState =
-                        new LegacyPermissionState.PermissionState(permissionState.getPermission(),
-                                permissionState.isGranted(), permissionState.getFlags());
-                if (permissionState.isRuntime()) {
-                    legacyState.putRuntimePermissionState(legacyPermissionState, userId);
-                } else if (userId == UserHandle.USER_SYSTEM) {
-                    legacyState.putInstallPermissionState(legacyPermissionState);
+                    final LegacyPermissionState.PermissionState legacyPermissionState =
+                            new LegacyPermissionState.PermissionState(
+                                    permissionState.getPermission(), permissionState.isGranted(),
+                                    permissionState.getFlags());
+                    if (permissionState.isRuntime()) {
+                        legacyState.putRuntimePermissionState(legacyPermissionState, userId);
+                    } else if (userId == UserHandle.USER_SYSTEM) {
+                        legacyState.putInstallPermissionState(legacyPermissionState);
+                    }
                 }
             }
         }
@@ -4683,12 +4726,15 @@
     private int[] getGidsForUid(int uid) {
         final int appId = UserHandle.getAppId(uid);
         final int userId = UserHandle.getUserId(uid);
-        final UidPermissionState uidState = getUidState(appId, userId);
-        if (uidState == null) {
-            Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + userId);
-            return EMPTY_INT_ARRAY;
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(appId, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+                        + userId);
+                return EMPTY_INT_ARRAY;
+            }
+            return uidState.computeGids(mGlobalGids, userId);
         }
-        return uidState.computeGids(mGlobalGids, userId);
     }
 
     private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index 82ab954..38e8955 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -24,8 +24,6 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -35,29 +33,23 @@
  * Permission state for a UID.
  */
 public final class UidPermissionState {
-    @NonNull
-    private final Object mLock = new Object();
-
     private boolean mMissing;
 
-    @GuardedBy("mLock")
     @Nullable
     private ArrayMap<String, PermissionState> mPermissions;
 
     public UidPermissionState() {}
 
     public UidPermissionState(@NonNull UidPermissionState other) {
-        synchronized (mLock) {
-            mMissing = other.mMissing;
+        mMissing = other.mMissing;
 
-            if (other.mPermissions != null) {
-                mPermissions = new ArrayMap<>();
-                final int permissionsSize = other.mPermissions.size();
-                for (int i = 0; i < permissionsSize; i++) {
-                    final String name = other.mPermissions.keyAt(i);
-                    final PermissionState permissionState = other.mPermissions.valueAt(i);
-                    mPermissions.put(name, new PermissionState(permissionState));
-                }
+        if (other.mPermissions != null) {
+            mPermissions = new ArrayMap<>();
+            final int permissionsSize = other.mPermissions.size();
+            for (int i = 0; i < permissionsSize; i++) {
+                final String name = other.mPermissions.keyAt(i);
+                final PermissionState permissionState = other.mPermissions.valueAt(i);
+                mPermissions.put(name, new PermissionState(permissionState));
             }
         }
     }
@@ -66,11 +58,9 @@
      * Reset the internal state of this object.
      */
     public void reset() {
-        synchronized (mLock) {
-            mMissing = false;
-            mPermissions = null;
-            invalidateCache();
-        }
+        mMissing = false;
+        mPermissions = null;
+        invalidateCache();
     }
 
     /**
@@ -97,9 +87,7 @@
      */
     @Deprecated
     public boolean hasPermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            return mPermissions != null && mPermissions.containsKey(name);
-        }
+        return mPermissions != null && mPermissions.containsKey(name);
     }
 
     /**
@@ -109,19 +97,17 @@
      */
     @Deprecated
     public boolean hasPermissionState(@NonNull ArraySet<String> names) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            final int namesSize = names.size();
-            for (int i = 0; i < namesSize; i++) {
-                final String name = names.valueAt(i);
-                if (mPermissions.containsKey(name)) {
-                    return true;
-                }
-            }
+        if (mPermissions == null) {
             return false;
         }
+        final int namesSize = names.size();
+        for (int i = 0; i < namesSize; i++) {
+            final String name = names.valueAt(i);
+            if (mPermissions.containsKey(name)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -132,28 +118,24 @@
      */
     @Nullable
     public PermissionState getPermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return null;
-            }
-            return mPermissions.get(name);
+        if (mPermissions == null) {
+            return null;
         }
+        return mPermissions.get(name);
     }
 
     @NonNull
     private PermissionState getOrCreatePermissionState(@NonNull BasePermission permission) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                mPermissions = new ArrayMap<>();
-            }
-            final String name = permission.getName();
-            PermissionState permissionState = mPermissions.get(name);
-            if (permissionState == null) {
-                permissionState = new PermissionState(permission);
-                mPermissions.put(name, permissionState);
-            }
-            return permissionState;
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
         }
+        final String name = permission.getName();
+        PermissionState permissionState = mPermissions.get(name);
+        if (permissionState == null) {
+            permissionState = new PermissionState(permission);
+            mPermissions.put(name, permissionState);
+        }
+        return permissionState;
     }
 
     /**
@@ -163,12 +145,10 @@
      */
     @NonNull
     public List<PermissionState> getPermissionStates() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return Collections.emptyList();
-            }
-            return new ArrayList<>(mPermissions.values());
+        if (mPermissions == null) {
+            return Collections.emptyList();
         }
+        return new ArrayList<>(mPermissions.values());
     }
 
     /**
@@ -179,20 +159,18 @@
      * @param flags the permission flags
      */
     public void putPermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
-        synchronized (mLock) {
-            final String name = permission.getName();
-            if (mPermissions == null) {
-                mPermissions = new ArrayMap<>();
-            } else {
-                mPermissions.remove(name);
-            }
-            final PermissionState permissionState = new PermissionState(permission);
-            if (granted) {
-                permissionState.grant();
-            }
-            permissionState.updateFlags(flags, flags);
-            mPermissions.put(name, permissionState);
+        final String name = permission.getName();
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
+        } else {
+            mPermissions.remove(name);
         }
+        final PermissionState permissionState = new PermissionState(permission);
+        if (granted) {
+            permissionState.grant();
+        }
+        permissionState.updateFlags(flags, flags);
+        mPermissions.put(name, permissionState);
     }
 
     /**
@@ -202,16 +180,14 @@
      * @return whether the permission state changed
      */
     public boolean removePermissionState(@NonNull String name) {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            boolean changed = mPermissions.remove(name) != null;
-            if (changed && mPermissions.isEmpty()) {
-                mPermissions = null;
-            }
-            return changed;
+        if (mPermissions == null) {
+            return false;
         }
+        final boolean changed = mPermissions.remove(name) != null;
+        if (changed && mPermissions.isEmpty()) {
+            mPermissions = null;
+        }
+        return changed;
     }
 
     /**
@@ -232,22 +208,20 @@
      */
     @NonNull
     public Set<String> getGrantedPermissions() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return Collections.emptySet();
-            }
-
-            Set<String> permissions = new ArraySet<>(mPermissions.size());
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                PermissionState permissionState = mPermissions.valueAt(i);
-
-                if (permissionState.isGranted()) {
-                    permissions.add(permissionState.getName());
-                }
-            }
-            return permissions;
+        if (mPermissions == null) {
+            return Collections.emptySet();
         }
+
+        final Set<String> permissions = new ArraySet<>(mPermissions.size());
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            final PermissionState permissionState = mPermissions.valueAt(i);
+
+            if (permissionState.isGranted()) {
+                permissions.add(permissionState.getName());
+            }
+        }
+        return permissions;
     }
 
     /**
@@ -257,7 +231,7 @@
      * @return whether the permission grant state changed
      */
     public boolean grantPermission(@NonNull BasePermission permission) {
-        PermissionState permissionState = getOrCreatePermissionState(permission);
+        final PermissionState permissionState = getOrCreatePermissionState(permission);
         return permissionState.grant();
     }
 
@@ -319,37 +293,33 @@
         if (flagMask == 0) {
             return false;
         }
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            boolean anyChanged = false;
-            for (int i = mPermissions.size() - 1; i >= 0; i--) {
-                final PermissionState permissionState = mPermissions.valueAt(i);
-                final boolean changed = permissionState.updateFlags(flagMask, flagValues);
-                if (changed && permissionState.isDefault()) {
-                    mPermissions.removeAt(i);
-                }
-                anyChanged |= changed;
-            }
-            return anyChanged;
+        if (mPermissions == null) {
+            return false;
         }
+        boolean anyChanged = false;
+        for (int i = mPermissions.size() - 1; i >= 0; i--) {
+            final PermissionState permissionState = mPermissions.valueAt(i);
+            final boolean changed = permissionState.updateFlags(flagMask, flagValues);
+            if (changed && permissionState.isDefault()) {
+                mPermissions.removeAt(i);
+            }
+            anyChanged |= changed;
+        }
+        return anyChanged;
     }
 
     public boolean isPermissionReviewRequired() {
-        synchronized (mLock) {
-            if (mPermissions == null) {
-                return false;
-            }
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                final PermissionState permission = mPermissions.valueAt(i);
-                if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                    return true;
-                }
-            }
+        if (mPermissions == null) {
             return false;
         }
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            final PermissionState permission = mPermissions.valueAt(i);
+            if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -360,24 +330,22 @@
      */
     @NonNull
     public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
-        synchronized (mLock) {
-            IntArray gids = IntArray.wrap(globalGids);
-            if (mPermissions == null) {
-                return gids.toArray();
-            }
-            final int permissionsSize = mPermissions.size();
-            for (int i = 0; i < permissionsSize; i++) {
-                PermissionState permissionState = mPermissions.valueAt(i);
-                if (!permissionState.isGranted()) {
-                    continue;
-                }
-                final int[] permissionGids = permissionState.computeGids(userId);
-                if (permissionGids.length != 0) {
-                    gids.addAll(permissionGids);
-                }
-            }
+        IntArray gids = IntArray.wrap(globalGids);
+        if (mPermissions == null) {
             return gids.toArray();
         }
+        final int permissionsSize = mPermissions.size();
+        for (int i = 0; i < permissionsSize; i++) {
+            PermissionState permissionState = mPermissions.valueAt(i);
+            if (!permissionState.isGranted()) {
+                continue;
+            }
+            final int[] permissionGids = permissionState.computeGids(userId);
+            if (permissionGids.length != 0) {
+                gids.addAll(permissionGids);
+            }
+        }
+        return gids.toArray();
     }
 
     static void invalidateCache() {
diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
index 7f55cb1..2c741cf 100644
--- a/services/core/java/com/android/server/pm/permission/UserPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java
@@ -23,8 +23,6 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.GuardedBy;
-
 /**
  * Permission state for a user.
  */
@@ -33,71 +31,52 @@
      * Whether the install permissions have been granted to a package, so that no install
      * permissions should be added to it unless the package is upgraded.
      */
-    @GuardedBy("mLock")
     @NonNull
     private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();
 
     /**
      * Maps from app ID to {@link UidPermissionState}.
      */
-    @GuardedBy("mLock")
     @NonNull
     private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();
 
-    @NonNull
-    private final Object mLock;
-
-    public UserPermissionState(@NonNull Object lock) {
-        mLock = lock;
-    }
-
     public boolean areInstallPermissionsFixed(@NonNull String packageName) {
-        synchronized (mLock) {
-            return mInstallPermissionsFixed.contains(packageName);
-        }
+        return mInstallPermissionsFixed.contains(packageName);
     }
 
     public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
-        synchronized (mLock) {
-            if (fixed) {
-                mInstallPermissionsFixed.add(packageName);
-            } else {
-                mInstallPermissionsFixed.remove(packageName);
-            }
+        if (fixed) {
+            mInstallPermissionsFixed.add(packageName);
+        } else {
+            mInstallPermissionsFixed.remove(packageName);
         }
     }
 
     @Nullable
     public UidPermissionState getUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            return mUidStates.get(appId);
-        }
+        return mUidStates.get(appId);
     }
 
     @NonNull
     public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            UidPermissionState uidState = mUidStates.get(appId);
-            if (uidState == null) {
-                uidState = new UidPermissionState();
-                mUidStates.put(appId, uidState);
-            }
-            return uidState;
+        UidPermissionState uidState = mUidStates.get(appId);
+        if (uidState == null) {
+            uidState = new UidPermissionState();
+            mUidStates.put(appId, uidState);
         }
+        return uidState;
     }
 
     public void removeUidState(@AppIdInt int appId) {
         checkAppId(appId);
-        synchronized (mLock) {
-            mUidStates.delete(appId);
-        }
+        mUidStates.delete(appId);
     }
 
     private void checkAppId(@AppIdInt int appId) {
         if (UserHandle.getUserId(appId) != 0) {
-            throw new IllegalArgumentException(appId + " is not an app ID");
+            throw new IllegalArgumentException("Invalid app ID " + appId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
new file mode 100644
index 0000000..17dba7d
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsLocationCoarseTestCases"
+    },
+    {
+      "name": "CtsLocationFineTestCases"
+    },
+    {
+      "name": "CtsLocationNoneTestCases"
+    },
+    {
+      "name": "FrameworksMockingServicesTests",
+      "options": [
+        {"include-filter": "com.android.server.location"}
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index a4fa745..44a6336 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -25,6 +25,7 @@
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
 import android.content.rollback.RollbackInfo;
 import android.os.UserHandle;
+import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseIntArray;
 
@@ -37,6 +38,7 @@
 import org.json.JSONObject;
 
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Files;
@@ -66,8 +68,6 @@
     //
     // * XXX, YYY are the rollbackIds for the corresponding rollbacks.
     // * rollback.json contains all relevant metadata for the rollback.
-    //
-    // TODO: Use AtomicFile for all the .json files?
     private final File mRollbackDataDir;
 
     RollbackStore(File rollbackDataDir) {
@@ -259,6 +259,8 @@
      * Saves the given rollback to persistent storage.
      */
     static void saveRollback(Rollback rollback) {
+        FileOutputStream fos = null;
+        AtomicFile file = new AtomicFile(new File(rollback.getBackupDir(), "rollback.json"));
         try {
             JSONObject dataJson = new JSONObject();
             dataJson.put("info", rollbackInfoToJson(rollback.info));
@@ -272,11 +274,16 @@
             dataJson.putOpt(
                     "extensionVersions", extensionVersionsToJson(rollback.getExtensionVersions()));
 
-            PrintWriter pw = new PrintWriter(new File(rollback.getBackupDir(), "rollback.json"));
+            fos = file.startWrite();
+            PrintWriter pw = new PrintWriter(fos);
             pw.println(dataJson.toString());
             pw.close();
+            file.finishWrite(fos);
         } catch (JSONException | IOException e) {
             Slog.e(TAG, "Unable to save rollback for: " + rollback.info.getRollbackId(), e);
+            if (fos != null) {
+                file.failWrite(fos);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
index 7b6c656..51b00fa 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java
@@ -236,7 +236,8 @@
 
         @Override
         public void unloadModel(int modelHandle) throws RemoteException {
-            enforcePermissions();
+            // Unloading a model does not require special permissions. Having a handle to the
+            // session is sufficient.
             mDelegate.unloadModel(modelHandle);
 
         }
@@ -250,7 +251,8 @@
 
         @Override
         public void stopRecognition(int modelHandle) throws RemoteException {
-            enforcePermissions();
+            // Stopping a model does not require special permissions. Having a handle to the
+            // session is sufficient.
             mDelegate.stopRecognition(modelHandle);
         }
 
@@ -284,7 +286,8 @@
 
         @Override
         public void detach() throws RemoteException {
-            enforcePermissions();
+            // Detaching does not require special permissions. Having a handle to the session is
+            // sufficient.
             mDelegate.detach();
         }
 
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index 9e76bc1..1f73977 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -28,6 +28,8 @@
 import android.app.time.TimeZoneConfiguration;
 import android.os.UserHandle;
 
+import com.android.internal.util.Preconditions;
+
 import java.util.Objects;
 
 /**
@@ -40,6 +42,7 @@
     private final @UserIdInt int mUserId;
     private final boolean mUserConfigAllowed;
     private final boolean mAutoDetectionSupported;
+    private final boolean mGeoDetectionSupported;
     private final boolean mAutoDetectionEnabled;
     private final boolean mLocationEnabled;
     private final boolean mGeoDetectionEnabled;
@@ -48,9 +51,13 @@
         mUserId = builder.mUserId;
         mUserConfigAllowed = builder.mUserConfigAllowed;
         mAutoDetectionSupported = builder.mAutoDetectionSupported;
+        mGeoDetectionSupported = builder.mGeoDetectionSupported;
         mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
         mLocationEnabled = builder.mLocationEnabled;
         mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+        // if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
+        // cannot be true if mAutoDetectionSupported == false
+        Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
     }
 
     /** Returns the ID of the user this configuration is associated with. */
@@ -69,11 +76,16 @@
         return mUserConfigAllowed;
     }
 
-    /** Returns true if the device supports some form of auto time zone detection. */
+    /** Returns true if the device supports any form of auto time zone detection. */
     public boolean isAutoDetectionSupported() {
         return mAutoDetectionSupported;
     }
 
+    /** Returns true if the device supports geolocation time zone detection. */
+    public boolean isGeoDetectionSupported() {
+        return mGeoDetectionSupported;
+    }
+
     /** Returns the value of the auto time zone detection enabled setting. */
     public boolean getAutoDetectionEnabledSetting() {
         return mAutoDetectionEnabled;
@@ -101,10 +113,10 @@
      * distinct from the raw setting value.
      */
     public boolean getGeoDetectionEnabledBehavior() {
-        if (getAutoDetectionEnabledBehavior()) {
-            return mLocationEnabled && mGeoDetectionEnabled;
-        }
-        return false;
+        return getAutoDetectionEnabledBehavior()
+                && isGeoDetectionSupported()
+                && isLocationEnabled()
+                && getGeoDetectionEnabledSetting();
     }
 
     /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */
@@ -121,10 +133,10 @@
 
         // Automatic time zone detection is only supported on devices if there is a telephony
         // network available or geolocation time zone detection is possible.
-        boolean deviceHasTimeZoneDetection = isAutoDetectionSupported();
+        boolean deviceHasAutoTimeZoneDetection = isAutoDetectionSupported();
 
         final int configureAutoDetectionEnabledCapability;
-        if (!deviceHasTimeZoneDetection) {
+        if (!deviceHasAutoTimeZoneDetection) {
             configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
         } else if (!allowConfigDateTime) {
             configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
@@ -133,12 +145,13 @@
         }
         builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability);
 
+        boolean deviceHasLocationTimeZoneDetection = isGeoDetectionSupported();
         final int configureGeolocationDetectionEnabledCapability;
-        if (!deviceHasTimeZoneDetection) {
+        if (!deviceHasLocationTimeZoneDetection) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
         } else if (!allowConfigDateTime) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
-        } else if (!isLocationEnabled()) {
+        } else if (!mAutoDetectionEnabled || !isLocationEnabled()) {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
         } else {
             configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
@@ -199,6 +212,7 @@
         return mUserId == that.mUserId
                 && mUserConfigAllowed == that.mUserConfigAllowed
                 && mAutoDetectionSupported == that.mAutoDetectionSupported
+                && mGeoDetectionSupported == that.mGeoDetectionSupported
                 && mAutoDetectionEnabled == that.mAutoDetectionEnabled
                 && mLocationEnabled == that.mLocationEnabled
                 && mGeoDetectionEnabled == that.mGeoDetectionEnabled;
@@ -207,7 +221,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
-                mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled);
+                mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled,
+                mGeoDetectionEnabled);
     }
 
     @Override
@@ -216,6 +231,7 @@
                 + "mUserId=" + mUserId
                 + ", mUserConfigAllowed=" + mUserConfigAllowed
                 + ", mAutoDetectionSupported=" + mAutoDetectionSupported
+                + ", mGeoDetectionSupported=" + mGeoDetectionSupported
                 + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled
                 + ", mLocationEnabled=" + mLocationEnabled
                 + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled
@@ -228,8 +244,10 @@
     public static class Builder {
 
         private final @UserIdInt int mUserId;
+
         private boolean mUserConfigAllowed;
         private boolean mAutoDetectionSupported;
+        private boolean mGeoDetectionSupported;
         private boolean mAutoDetectionEnabled;
         private boolean mLocationEnabled;
         private boolean mGeoDetectionEnabled;
@@ -248,6 +266,7 @@
             this.mUserId = toCopy.mUserId;
             this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
             this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+            this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
             this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
             this.mLocationEnabled = toCopy.mLocationEnabled;
             this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
@@ -262,7 +281,7 @@
         }
 
         /**
-         * Sets whether automatic time zone detection is supported on this device.
+         * Sets whether any form of automatic time zone detection is supported on this device.
          */
         public Builder setAutoDetectionSupported(boolean supported) {
             mAutoDetectionSupported = supported;
@@ -270,6 +289,14 @@
         }
 
         /**
+         * Sets whether geolocation time zone detection is supported on this device.
+         */
+        public Builder setGeoDetectionSupported(boolean supported) {
+            mGeoDetectionSupported = supported;
+            return this;
+        }
+
+        /**
          * Sets the value of the automatic time zone detection enabled setting for this device.
          */
         public Builder setAutoDetectionEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 964dbec..941be0e 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -119,13 +119,13 @@
 
     @Override
     public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
-        boolean geoDetectionEnabled = mGeoDetectionFeatureEnabled && isGeoDetectionEnabled(userId);
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(isUserConfigAllowed(userId))
                 .setAutoDetectionSupported(isAutoDetectionSupported())
+                .setGeoDetectionSupported(isGeoDetectionSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
                 .setLocationEnabled(isLocationEnabled(userId))
-                .setGeoDetectionEnabled(geoDetectionEnabled)
+                .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                 .build();
     }
 
@@ -170,7 +170,11 @@
             final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
             setAutoDetectionEnabled(autoDetectionEnabled);
 
-            if (mGeoDetectionFeatureEnabled) {
+            // Avoid writing the geo detection enabled setting for devices that do not support geo
+            // time zone detection: if we wrote it down then we'd set the value explicitly, which
+            // would prevent detecting "default" later. That might influence what happens on later
+            // releases that support geo detection on the same hardware.
+            if (isGeoDetectionSupported()) {
                 final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                 setGeoDetectionEnabled(userId, geoTzDetectionEnabled);
             }
@@ -183,7 +187,11 @@
     }
 
     private boolean isAutoDetectionSupported() {
-        return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled;
+        return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
+    }
+
+    private boolean isGeoDetectionSupported() {
+        return mGeoDetectionFeatureEnabled;
     }
 
     private boolean isAutoDetectionEnabled() {
@@ -199,10 +207,10 @@
     }
 
     private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
-        final boolean locationEnabled = isLocationEnabled(userId);
+        final boolean geoDetectionEnabledByDefault = false;
         return Settings.Secure.getIntForUser(mCr,
                 Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
-                locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0;
+                (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
     }
 
     private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d09cd38..68a086d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -30,6 +30,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -58,11 +59,14 @@
     private static final String TAG = "TimeZoneDetectorService";
 
     /**
-     * A compile time constant "feature switch" for enabling / disabling location-based time zone
-     * detection on Android. If this is {@code false}, there should be few / little changes in
-     * behavior with previous releases and little overhead associated with geolocation components.
+     * A "feature switch" for enabling / disabling location-based time zone detection. If this is
+     * {@code false}, there should be few / little changes in behavior with previous releases and
+     * little overhead associated with geolocation components.
+     * TODO(b/151304765) Remove this when the feature is on for all.
      */
-    public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+    public static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED =
+            SystemProperties.getBoolean(
+                    "persist.sys.location_time_zone_detection_feature_enabled", false);
 
     /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7b044ed..149dbd0 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -54,7 +54,7 @@
 import android.media.tv.ITvInputServiceCallback;
 import android.media.tv.ITvInputSession;
 import android.media.tv.ITvInputSessionCallback;
-import android.media.tv.TvChannelInfo;
+import android.media.tv.TunedInfo;
 import android.media.tv.TvContentRating;
 import android.media.tv.TvContentRatingSystemInfo;
 import android.media.tv.TvContract;
@@ -90,7 +90,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -114,7 +113,7 @@
     private static final boolean DEBUG = false;
     private static final String TAG = "TvInputManagerService";
     private static final String DVB_DIRECTORY = "/dev/dvb";
-    private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+    private static final int APP_TAG_SELF = TunedInfo.APP_TAG_SELF;
     private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
             "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
 
@@ -860,9 +859,9 @@
             try {
                 ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i);
                 Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback);
-                List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked(
+                List<TunedInfo> infos = getCurrentTunedInfosInternalLocked(
                         userState, pidUid.first, pidUid.second);
-                callback.onCurrentTvChannelInfosUpdated(infos);
+                callback.onCurrentTunedInfosUpdated(infos);
             } catch (RemoteException e) {
                 Slog.e(TAG, "failed to report updated current channel infos to callback", e);
             }
@@ -2097,14 +2096,14 @@
         }
 
         @Override
-        public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+        public List<TunedInfo> getCurrentTunedInfos(@UserIdInt int userId) {
             int callingPid = Binder.getCallingPid();
             int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
                     "getTvCurrentChannelInfos");
             synchronized (mLock) {
                 UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                return getCurrentTvChannelInfosInternalLocked(userState, callingPid, callingUid);
+                return getCurrentTunedInfosInternalLocked(userState, callingPid, callingUid);
             }
         }
 
@@ -2278,9 +2277,9 @@
         }
     }
 
-    private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(
+    private List<TunedInfo> getCurrentTunedInfosInternalLocked(
             UserState userState, int callingPid, int callingUid) {
-        List<TvChannelInfo> channelInfos = new ArrayList<>();
+        List<TunedInfo> channelInfos = new ArrayList<>();
         boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid);
         for (SessionState state : userState.sessionStateMap.values()) {
             if (state.isCurrent) {
@@ -2288,7 +2287,7 @@
                 int appType;
                 if (state.callingUid == callingUid) {
                     appTag = APP_TAG_SELF;
-                    appType = TvChannelInfo.APP_TYPE_SELF;
+                    appType = TunedInfo.APP_TYPE_SELF;
                 } else {
                     appTag = userState.mAppTagMap.get(state.callingUid);
                     if (appTag == null) {
@@ -2296,10 +2295,10 @@
                         userState.mAppTagMap.put(state.callingUid, appTag);
                     }
                     appType = isSystemApp(state.componentName.getPackageName())
-                            ? TvChannelInfo.APP_TYPE_SYSTEM
-                            : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+                            ? TunedInfo.APP_TYPE_SYSTEM
+                            : TunedInfo.APP_TYPE_NON_SYSTEM;
                 }
-                channelInfos.add(new TvChannelInfo(
+                channelInfos.add(new TunedInfo(
                         state.inputId,
                         watchedProgramsAccess ? state.currentChannel : null,
                         state.isRecordingSession,
diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9868fc1..8f2e60e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3222,6 +3222,7 @@
         getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
         mWmService.mTaskSnapshotController.onAppRemoved(this);
         mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
+        mStackSupervisor.mStoppingActivities.remove(this);
         waitingToShow = false;
 
         // TODO(b/169035022): move to a more-appropriate place.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 28e71fa..c8a8f81 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1358,6 +1358,12 @@
             }
             return false;
         }
+        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
+        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
+            Slog.w(TAG, "Background activity start for " + callingPackage
+                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
+            return false;
+        }
         // If we don't have callerApp at this point, no caller was provided to startActivity().
         // That's the case for PendingIntent-based starts, since the creator's process might not be
         // up and alive. If that's the case, we retrieve the WindowProcessController for the send()
@@ -1394,12 +1400,6 @@
                 }
             }
         }
-        // don't abort if the callingUid has SYSTEM_ALERT_WINDOW permission
-        if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
-            Slog.w(TAG, "Background activity start for " + callingPackage
-                    + " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
-            return false;
-        }
         // anything that has fallen through would currently be aborted
         Slog.w(TAG, "Background activity start [callingPackage: " + callingPackage
                 + "; callingUid: " + callingUid
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index 4e742b9..af6c255 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -24,8 +24,8 @@
  */
 public interface BackgroundActivityStartCallback {
     /**
-     * The token that allowed the activity start that triggered {@link
-     * #onExclusiveTokenActivityStart()}.
+     * The token for which this callback is responsible for deciding whether the app can start
+     * background activities or not.
      *
      * Ideally this should just return a final variable, don't do anything costly here (don't hold
      * any locks).
@@ -33,7 +33,10 @@
     IBinder getToken();
 
     /**
-     * Called when the background activity start happens.
+     * Returns true if the background activity start due to originating token in {@link #getToken()}
+     * should be allowed or not.
+     *
+     * This will be called holding the WM lock, don't do anything costly here.
      */
-    void onExclusiveTokenActivityStart(String packageName);
+    boolean isActivityStartAllowed(int uid, String packageName);
 }
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 8bd42f0..1718b2a 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -370,6 +370,9 @@
                 Comparator.comparingInt(WindowToken::getWindowLayerFromType);
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
+            if (!w.isVisible() || !w.mLegacyPolicyVisibilityAfterAnim) {
+                return false;
+            }
             final WindowManagerPolicy policy = mWmService.mPolicy;
             if (policy.isKeyguardHostWindow(w.mAttrs)) {
                 if (mWmService.mKeyguardGoingAway) {
@@ -405,6 +408,7 @@
 
         @Override
         int getOrientation(int candidate) {
+            mLastOrientationSource = null;
             // Find a window requesting orientation.
             final WindowState win = getWindow(mGetOrientingWindow);
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index dafa07e..e7f0e3e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -253,10 +253,23 @@
 
     ActivityTaskManagerService mAtmService;
 
-    /** Unique identifier of this display. */
+    /**
+     * Unique logical identifier of this display.
+     *
+     * @see DisplayInfo#displayId
+     */
     final int mDisplayId;
 
     /**
+     * Unique physical identifier of this display. Unlike {@link #mDisplayId} this value can change
+     * at runtime if the underlying physical display changes.
+     *
+     * @see DisplayInfo#uniqueId
+     */
+    @Nullable
+    String mCurrentUniqueDisplayId;
+
+    /**
      * We organize all top-level Surfaces into the following layer.
      * It contains a few Surfaces which are always on top of others, and omitted from
      * Screen-Magnification, for example the strict mode flash or the fullscreen magnification
@@ -916,6 +929,7 @@
         mAtmService = mWmService.mAtmService;
         mDisplay = display;
         mDisplayId = display.getDisplayId();
+        mCurrentUniqueDisplayId = display.getUniqueId();
         mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
         mWallpaperController = new WallpaperController(mWmService, this);
         display.getDisplayInfo(mDisplayInfo);
@@ -2417,13 +2431,20 @@
         final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
         final int newDensity = mDisplayInfo.logicalDensityDpi;
         final DisplayCutout newCutout = mDisplayInfo.displayCutout;
+        final String newUniqueId = mDisplayInfo.uniqueId;
 
         final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
                 || mInitialDisplayHeight != newHeight
                 || mInitialDisplayDensity != mDisplayInfo.logicalDensityDpi
                 || !Objects.equals(mInitialDisplayCutout, newCutout);
+        final boolean physicalDisplayChanged = !newUniqueId.equals(mCurrentUniqueDisplayId);
 
-        if (displayMetricsChanged) {
+        if (displayMetricsChanged || physicalDisplayChanged) {
+            if (physicalDisplayChanged) {
+                // Reapply the window settings as the underlying physical display has changed.
+                mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+            }
+
             // If there is an override set for base values - use it, otherwise use new values.
             updateBaseDisplayMetrics(mIsSizeForced ? mBaseDisplayWidth : newWidth,
                     mIsSizeForced ? mBaseDisplayHeight : newHeight,
@@ -2434,6 +2455,7 @@
             mInitialDisplayHeight = newHeight;
             mInitialDisplayDensity = newDensity;
             mInitialDisplayCutout = newCutout;
+            mCurrentUniqueDisplayId = newUniqueId;
             reconfigureDisplayLocked();
         }
     }
@@ -2452,6 +2474,10 @@
 
     /** Update base (override) display metrics. */
     void updateBaseDisplayMetrics(int baseWidth, int baseHeight, int baseDensity) {
+        final int originalWidth = mBaseDisplayWidth;
+        final int originalHeight = mBaseDisplayHeight;
+        final int originalDensity = mBaseDisplayDensity;
+
         mBaseDisplayWidth = baseWidth;
         mBaseDisplayHeight = baseHeight;
         mBaseDisplayDensity = baseDensity;
@@ -2466,9 +2492,11 @@
             }
         }
 
-        mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
-
-        updateBounds();
+        if (mBaseDisplayWidth != originalWidth || mBaseDisplayHeight != originalHeight
+                || mBaseDisplayDensity != originalDensity) {
+            mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+            updateBounds();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3ff369a3..4e7e0ba 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1040,9 +1040,12 @@
         }
 
         if (attrs.providesInsetsTypes != null) {
-            mContext.enforcePermission(
-                    android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
-                    "DisplayPolicy");
+            // Recents component is allowed to add inset types.
+            if (!mService.mAtmInternal.isCallerRecents(callingUid)) {
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                        "DisplayPolicy");
+            }
             enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providesInsetsTypes);
 
             for (@InternalInsetsType int insetType : attrs.providesInsetsTypes) {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index c8c83a6..04e37fa 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -385,13 +385,16 @@
         dc.getDisplayRotation().restoreSettings(entry.mUserRotationMode,
                 entry.mUserRotation, entry.mFixedToUserRotation);
 
-        if (entry.mForcedDensity != 0) {
-            dc.mBaseDisplayDensity = entry.mForcedDensity;
-        }
-        if (entry.mForcedWidth != 0 && entry.mForcedHeight != 0) {
-            dc.updateBaseDisplayMetrics(entry.mForcedWidth, entry.mForcedHeight,
-                    dc.mBaseDisplayDensity);
-        }
+        final boolean hasDensityOverride = entry.mForcedDensity != 0;
+        final boolean hasSizeOverride = entry.mForcedWidth != 0 && entry.mForcedHeight != 0;
+        dc.mIsDensityForced = hasDensityOverride;
+        dc.mIsSizeForced = hasSizeOverride;
+
+        final int width = hasSizeOverride ? entry.mForcedWidth : dc.mBaseDisplayWidth;
+        final int height = hasSizeOverride ? entry.mForcedHeight : dc.mBaseDisplayHeight;
+        final int density = hasDensityOverride ? entry.mForcedDensity : dc.mBaseDisplayDensity;
+        dc.updateBaseDisplayMetrics(width, height, density);
+
         dc.mDisplayScalingDisabled = entry.mForcedScalingMode == FORCE_SCALING_MODE_DISABLED;
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 803a533..143b657 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -265,6 +265,7 @@
                 consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL;
                 break;
             case INPUT_CONSUMER_RECENTS_ANIMATION:
+                consumer.mWindowHandle.focusable = true;
                 break;
             default:
                 throw new IllegalArgumentException("Illegal input consumer : " + name
@@ -545,7 +546,7 @@
 
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
-                        mRecentsAnimationInputConsumer.mWindowHandle, focusable)) {
+                        mRecentsAnimationInputConsumer.mWindowHandle)) {
                     mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                     mAddRecentsAnimationInputConsumerHandle = false;
                 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index a40b310..4ad2575 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -389,9 +389,7 @@
         final int taskCount = visibleTasks.size();
         for (int i = 0; i < taskCount; i++) {
             final Task task = visibleTasks.get(i);
-            final WindowConfiguration config = task.getWindowConfiguration();
-            if (config.tasksAreFloating()
-                    || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+            if (skipAnimation(task)) {
                 continue;
             }
             addAnimation(task, !recentTaskIds.get(task.mTaskId));
@@ -434,6 +432,19 @@
         }
     }
 
+
+    /**
+     * Whether a task should be filtered from the recents animation. This can be true for tasks
+     * being displayed outside of recents.
+     */
+    private boolean skipAnimation(Task task) {
+        final WindowConfiguration config = task.getWindowConfiguration();
+        return task.isAlwaysOnTop()
+                || config.tasksAreFloating()
+                || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+    }
+
+
     @VisibleForTesting
     AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
         return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
@@ -529,8 +540,9 @@
 
     void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
         if (mRunner != null) {
-            // No need to send task appeared when the task target already exists.
-            if (isAnimatingTask(task)) {
+            // No need to send task appeared when the task target already exists, or when the
+            // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
+            if (isAnimatingTask(task) || skipAnimation(task)) {
                 return;
             }
             final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback);
@@ -827,15 +839,13 @@
                 && !isTargetApp(activity) && isAnimatingApp(activity);
     }
 
-    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
-            boolean focusable) {
+    boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) {
         // Update the input consumer touchable region to match the target app main window
         final WindowState targetAppMainWindow = mTargetActivityRecord != null
                 ? mTargetActivityRecord.findMainWindow()
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.focusable = focusable;
             inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 38ec924..95d8662 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2744,8 +2744,9 @@
             pw.print(prefix); pw.println("ContainerAnimator:");
             mSurfaceAnimator.dump(pw, prefix + "  ");
         }
-        if (mLastOrientationSource != null) {
+        if (mLastOrientationSource != null && this == mDisplayContent) {
             pw.println(prefix + "mLastOrientationSource=" + mLastOrientationSource);
+            pw.println(prefix + "deepestLastOrientationSource=" + getLastOrientationSource());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0b200e2..6a57844 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -207,7 +207,7 @@
                         final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                 entries.next();
                         final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
-                        if (!wc.isAttached()) {
+                        if (wc == null || !wc.isAttached()) {
                             Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                             continue;
                         }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index fb9bdf5..e8a7a9c1 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -223,7 +223,7 @@
     private boolean mRunningRemoteAnimation;
 
     @Nullable
-    private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+    private final BackgroundActivityStartCallback mBackgroundActivityStartCallback;
 
     /** The state for oom-adjustment calculation. */
     private final OomScoreReferenceState mOomRefState;
@@ -555,8 +555,7 @@
             return true;
         }
         // allow if the flag was explicitly set
-        if (!mBackgroundActivityStartTokens.isEmpty()) {
-            onBackgroundStartAllowedByToken();
+        if (isBackgroundStartAllowedByToken()) {
             if (DEBUG_ACTIVITY_STARTS) {
                 Slog.d(TAG, "[WindowProcessController(" + mPid
                         + ")] Activity start allowed: process allowed by token");
@@ -566,18 +565,29 @@
         return false;
     }
 
-    private void onBackgroundStartAllowedByToken() {
+    /**
+     * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
+     * the callback handles all the tokens, if so we ask the callback if the activity should be
+     * started, otherwise we allow.
+     */
+    private boolean isBackgroundStartAllowedByToken() {
+        if (mBackgroundActivityStartTokens.isEmpty()) {
+            return false;
+        }
         if (mBackgroundActivityStartCallback == null) {
-            return;
+            // We have tokens but no callback to decide => allow
+            return true;
         }
         IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
         for (IBinder token : mBackgroundActivityStartTokens.values()) {
             if (token != callbackToken) {
-                return;
+                // The callback doesn't handle all the tokens => allow
+                return true;
             }
         }
-        mAtm.mH.post(() ->
-                mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
+        // The callback handles all the tokens => callback decides
+        return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
+                mInfo.packageName);
     }
 
     private boolean isBoundByForegroundUid() {
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 160ad89..cbe0a42 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -30,7 +30,6 @@
 import static com.android.server.wm.WindowSurfaceControllerProto.LAYER;
 import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN;
 
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Debug;
 import android.os.Trace;
@@ -62,7 +61,6 @@
     private float mSurfaceY = 0;
     private int mSurfaceW = 0;
     private int mSurfaceH = 0;
-    private Rect mSurfaceCrop = new Rect(0, 0, -1, -1);
 
     // Initialize to the identity matrix.
     private float mLastDsdx = 1;
@@ -74,13 +72,6 @@
 
     private int mSurfaceLayer = 0;
 
-    // Surface flinger doesn't support crop rectangles where width or height is non-positive.
-    // However, we need to somehow handle the situation where the cropping would completely hide
-    // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
-    private boolean mHiddenForCrop = false;
-
-    // Initially a surface is hidden after just being created.
-    private boolean mHiddenForOtherReasons = true;
     private final String title;
 
     private final WindowManagerService mService;
@@ -140,13 +131,6 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
-    void reparentChildrenInTransaction(WindowSurfaceController other) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "REPARENT from: %s to: %s", this, other);
-        if ((mSurfaceControl != null) && (other.mSurfaceControl != null)) {
-            mSurfaceControl.reparentChildren(other.mSurfaceControl);
-        }
-    }
-
     void detachChildren() {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SEVER CHILDREN");
         mChildrenDetached = true;
@@ -157,7 +141,6 @@
 
     void hide(SurfaceControl.Transaction transaction, String reason) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
-        mHiddenForOtherReasons = true;
 
         mAnimator.destroyPreservedSurfaceLocked();
         if (mSurfaceShown) {
@@ -195,47 +178,6 @@
         }
     }
 
-    void setCropInTransaction(Rect clipRect, boolean recoveringMemory) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CROP %s: %s", clipRect.toShortString(), title);
-        try {
-            if (clipRect.width() > 0 && clipRect.height() > 0) {
-                if (!clipRect.equals(mSurfaceCrop)) {
-                    mSurfaceControl.setWindowCrop(clipRect);
-                    mSurfaceCrop.set(clipRect);
-                }
-                mHiddenForCrop = false;
-                updateVisibility();
-            } else {
-                mHiddenForCrop = true;
-                mAnimator.destroyPreservedSurfaceLocked();
-                updateVisibility();
-            }
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error setting crop surface of " + this
-                    + " crop=" + clipRect.toShortString(), e);
-            if (!recoveringMemory) {
-                mAnimator.reclaimSomeSurfaceMemory("crop", true);
-            }
-        }
-    }
-
-    void clearCropInTransaction(boolean recoveringMemory) {
-        ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE CLEAR CROP: %s", title);
-        try {
-            Rect clipRect = new Rect(0, 0, -1, -1);
-            if (mSurfaceCrop.equals(clipRect)) {
-                return;
-            }
-            mSurfaceControl.setWindowCrop(clipRect);
-            mSurfaceCrop.set(clipRect);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Error setting clearing crop of " + this, e);
-            if (!recoveringMemory) {
-                mAnimator.reclaimSomeSurfaceMemory("crop", true);
-            }
-        }
-    }
-
     void setPosition(SurfaceControl.Transaction t, float left, float top,
             boolean recoveringMemory) {
         final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
@@ -287,31 +229,6 @@
         }
     }
 
-    boolean setBufferSizeInTransaction(int width, int height, boolean recoveringMemory) {
-        final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
-        if (surfaceResized) {
-            mSurfaceW = width;
-            mSurfaceH = height;
-
-            try {
-                ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SIZE %dx%d: %s", width, height, title);
-                mSurfaceControl.setBufferSize(width, height);
-            } catch (RuntimeException e) {
-                // If something goes wrong with the surface (such
-                // as running out of memory), don't take down the
-                // entire system.
-                Slog.e(TAG, "Error resizing surface of " + title
-                        + " size=(" + width + "x" + height + ")", e);
-                if (!recoveringMemory) {
-                    mAnimator.reclaimSomeSurfaceMemory("size", true);
-                }
-                return false;
-            }
-            return true;
-        }
-        return false;
-    }
-
     boolean prepareToShowInTransaction(float alpha,
             float dsdx, float dtdx, float dsdy,
             float dtdy, boolean recoveringMemory) {
@@ -404,35 +321,15 @@
         }
     }
 
-    void getContainerRect(Rect rect) {
-        mAnimator.getContainerRect(rect);
-    }
-
     boolean showRobustlyInTransaction() {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SHOW (performLayout): %s", title);
         if (DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + this
                 + " during relayout");
-        mHiddenForOtherReasons = false;
-        return updateVisibility();
-    }
 
-    private boolean updateVisibility() {
-        if (mHiddenForCrop || mHiddenForOtherReasons) {
-            if (mSurfaceShown) {
-                hideSurface(mTmpTransaction);
-                SurfaceControl.mergeToGlobalTransaction(mTmpTransaction);
-            }
-            return false;
-        } else {
-            if (!mSurfaceShown) {
-                return showSurface();
-            } else {
-                return true;
-            }
+        if (mSurfaceShown) {
+            return true;
         }
-    }
 
-    private boolean showSurface() {
         try {
             setShown(true);
             mSurfaceControl.show();
@@ -442,15 +339,9 @@
         }
 
         mAnimator.reclaimSomeSurfaceMemory("show", true);
-
         return false;
     }
 
-    void deferTransactionUntil(SurfaceControl barrier, long frame) {
-        // TODO: Logging
-        mSurfaceControl.deferTransactionUntil(barrier, frame);
-    }
-
     void forceScaleableInTransaction(boolean force) {
         // -1 means we don't override the default or client specified
         // scaling mode.
@@ -472,7 +363,6 @@
         return mSurfaceControl.getContentFrameStats(outStats);
     }
 
-
     boolean hasSurface() {
         return mSurfaceControl != null;
     }
@@ -487,10 +377,6 @@
         }
     }
 
-    int getLayer() {
-        return mSurfaceLayer;
-    }
-
     boolean getShown() {
         return mSurfaceShown;
     }
@@ -507,14 +393,6 @@
         }
     }
 
-    float getX() {
-        return mSurfaceX;
-    }
-
-    float getY() {
-        return mSurfaceY;
-    }
-
     int getWidth() {
         return mSurfaceW;
     }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 4413dc3..c1d5f19 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -99,6 +99,7 @@
     jmethodID notifyInputChannelBroken;
     jmethodID notifyANR;
     jmethodID notifyFocusChanged;
+    jmethodID notifyUntrustedTouch;
     jmethodID filterInputEvent;
     jmethodID interceptKeyBeforeQueueing;
     jmethodID interceptMotionBeforeQueueingNonInteractive;
@@ -256,6 +257,7 @@
             const sp<IBinder>& token, const std::string& reason) override;
     void notifyInputChannelBroken(const sp<IBinder>& token) override;
     void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
+    void notifyUntrustedTouch(const std::string& obscuringPackage) override;
     bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
     void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
     void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) override;
@@ -755,6 +757,17 @@
     }
 }
 
+void NativeInputManager::notifyUntrustedTouch(const std::string& obscuringPackage) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+    ALOGD("notifyUntrustedTouch - obscuringPackage=%s", obscuringPackage.c_str());
+#endif
+    ATRACE_CALL();
+    JNIEnv* env = jniEnv();
+    jstring jPackage = env->NewStringUTF(obscuringPackage.c_str());
+    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyUntrustedTouch, jPackage);
+    checkAndClearExceptionFromCallback(env, "notifyUntrustedTouch");
+}
+
 void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
         const sp<IBinder>& newToken) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
@@ -1893,6 +1906,9 @@
     GET_METHOD_ID(gServiceClassInfo.notifyFocusChanged, clazz,
             "notifyFocusChanged", "(Landroid/os/IBinder;Landroid/os/IBinder;)V");
 
+    GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
+                  "(Ljava/lang/String;)V");
+
     GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz,
             "notifyANR",
             "(Landroid/view/InputApplicationHandle;Landroid/os/IBinder;Ljava/lang/String;)J");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b7d94c1..36ff974 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -411,37 +411,37 @@
     private static final int STATUS_BAR_DISABLE2_MASK =
             StatusBarManager.DISABLE2_QUICK_SETTINGS;
 
-    private static final Set<String> SECURE_SETTINGS_WHITELIST;
-    private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_WHITELIST;
-    private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
+    private static final Set<String> SECURE_SETTINGS_ALLOWLIST;
+    private static final Set<String> SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST;
+    private static final Set<String> GLOBAL_SETTINGS_ALLOWLIST;
     private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
-    private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
+    private static final Set<String> SYSTEM_SETTINGS_ALLOWLIST;
     private static final Set<Integer> DA_DISALLOWED_POLICIES;
     // A collection of user restrictions that are deprecated and should simply be ignored.
     private static final String AB_DEVICE_KEY = "ro.build.ab_update";
 
     static {
-        SECURE_SETTINGS_WHITELIST = new ArraySet<>();
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS);
-        SECURE_SETTINGS_WHITELIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
+        SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.SKIP_FIRST_USE_HINTS);
+        SECURE_SETTINGS_ALLOWLIST.add(Settings.Secure.INSTALL_NON_MARKET_APPS);
 
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST = new ArraySet<>();
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST.addAll(SECURE_SETTINGS_WHITELIST);
-        SECURE_SETTINGS_DEVICEOWNER_WHITELIST.add(Settings.Secure.LOCATION_MODE);
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST = new ArraySet<>();
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.addAll(SECURE_SETTINGS_ALLOWLIST);
+        SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.add(Settings.Secure.LOCATION_MODE);
 
-        GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_SLEEP_POLICY);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_MODE);
-        GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
+        GLOBAL_SETTINGS_ALLOWLIST = new ArraySet<>();
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.ADB_WIFI_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.AUTO_TIME_ZONE);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.DATA_ROAMING);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.USB_MASS_STORAGE_ENABLED);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_SLEEP_POLICY);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_MODE);
+        GLOBAL_SETTINGS_ALLOWLIST.add(Settings.Global.PRIVATE_DNS_SPECIFIER);
 
         GLOBAL_SETTINGS_DEPRECATED = new ArraySet<>();
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.BLUETOOTH_ON);
@@ -450,11 +450,11 @@
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.NETWORK_PREFERENCE);
         GLOBAL_SETTINGS_DEPRECATED.add(Settings.Global.WIFI_ON);
 
-        SYSTEM_SETTINGS_WHITELIST = new ArraySet<>();
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
-        SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
+        SYSTEM_SETTINGS_ALLOWLIST = new ArraySet<>();
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        SYSTEM_SETTINGS_ALLOWLIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
 
         DA_DISALLOWED_POLICIES = new ArraySet<>();
         DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
@@ -1733,7 +1733,7 @@
 
         final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
-            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+            public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 final int status = intent.getIntExtra(
                         PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
@@ -5660,7 +5660,7 @@
      */
     @Override
     public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown,
-            List<String> lockdownWhitelist)
+            List<String> lockdownAllowlist)
             throws SecurityException {
         Objects.requireNonNull(who, "ComponentName is null");
 
@@ -5675,10 +5675,10 @@
                         DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
             }
 
-            if (vpnPackage != null && lockdown && lockdownWhitelist != null) {
-                for (String packageName : lockdownWhitelist) {
+            if (vpnPackage != null && lockdown && lockdownAllowlist != null) {
+                for (String packageName : lockdownAllowlist) {
                     if (!isPackageInstalledForUser(packageName, userId)) {
-                        Slog.w(LOG_TAG, "Non-existent package in VPN whitelist: " + packageName);
+                        Slog.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
                         throw new ServiceSpecificException(
                                 DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
                     }
@@ -5686,7 +5686,7 @@
             }
             // If some package is uninstalled after the check above, it will be ignored by CM.
             if (!mInjector.getConnectivityManager().setAlwaysOnVpnPackageForUser(
-                    userId, vpnPackage, lockdown, lockdownWhitelist)) {
+                    userId, vpnPackage, lockdown, lockdownAllowlist)) {
                 throw new UnsupportedOperationException();
             }
             DevicePolicyEventLogger
@@ -5694,7 +5694,7 @@
                     .setAdmin(caller.getComponentName())
                     .setStrings(vpnPackage)
                     .setBoolean(lockdown)
-                    .setInt(lockdownWhitelist != null ? lockdownWhitelist.size() : 0)
+                    .setInt(lockdownAllowlist != null ? lockdownAllowlist.size() : 0)
                     .write();
         });
         synchronized (getLockObject()) {
@@ -5752,7 +5752,7 @@
     }
 
     @Override
-    public List<String> getAlwaysOnVpnLockdownWhitelist(ComponentName admin)
+    public List<String> getAlwaysOnVpnLockdownAllowlist(ComponentName admin)
             throws SecurityException {
         Objects.requireNonNull(admin, "ComponentName is null");
 
@@ -10475,7 +10475,7 @@
                 return;
             }
 
-            if (!GLOBAL_SETTINGS_WHITELIST.contains(setting)
+            if (!GLOBAL_SETTINGS_ALLOWLIST.contains(setting)
                     && !UserManager.isDeviceInDemoMode(mContext)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
@@ -10503,7 +10503,7 @@
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            if (!SYSTEM_SETTINGS_WHITELIST.contains(setting)) {
+            if (!SYSTEM_SETTINGS_ALLOWLIST.contains(setting)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
             }
@@ -10663,12 +10663,12 @@
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             if (isDeviceOwner(who, callingUserId)) {
-                if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)
+                if (!SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.contains(setting)
                         && !isCurrentUserDemo()) {
                     throw new SecurityException(String.format(
                             "Permission denial: Device owners cannot update %1$s", setting));
                 }
-            } else if (!SECURE_SETTINGS_WHITELIST.contains(setting) && !isCurrentUserDemo()) {
+            } else if (!SECURE_SETTINGS_ALLOWLIST.contains(setting) && !isCurrentUserDemo()) {
                 throw new SecurityException(String.format(
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
@@ -12484,7 +12484,7 @@
     @Override
     public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
         // As the caller is the system, it must specify the component name of the profile owner
-        // as a sanity / safety check.
+        // as a safety check.
         Objects.requireNonNull(who);
 
         if (!mHasFeature) {
@@ -12520,7 +12520,7 @@
     @GuardedBy("getLockObject()")
     private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(
             ComponentName who, int userId) {
-        // Sanity check: Make sure that the user has a profile owner and that the specified
+        // Make sure that the user has a profile owner and that the specified
         // component is the profile owner of that user.
         if (!isProfileOwner(who, userId)) {
             throw new IllegalArgumentException(String.format(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 177cb1a..275d8f1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -280,6 +280,8 @@
             "com.android.server.autofill.AutofillManagerService";
     private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS =
             "com.android.server.contentcapture.ContentCaptureManagerService";
+    private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
+            "com.android.server.musicrecognition.MusicRecognitionManagerService";
     private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
             "com.android.server.systemcaptions.SystemCaptionsManagerService";
     private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
@@ -1415,6 +1417,17 @@
                 t.traceEnd();
             }
 
+            if (deviceHasConfigString(context,
+                    R.string.config_defaultMusicRecognitionService)) {
+                t.traceBegin("StartMusicRecognitionManagerService");
+                mSystemServiceManager.startService(MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG,
+                        "MusicRecognitionManagerService not defined by OEM or disabled by flag");
+            }
+
+
             startContentCaptureService(context, t);
             startAttentionService(context, t);
 
diff --git a/services/musicrecognition/Android.bp b/services/musicrecognition/Android.bp
new file mode 100644
index 0000000..39b5bb6
--- /dev/null
+++ b/services/musicrecognition/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "services.musicsearch-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.musicsearch",
+    defaults: ["services_defaults"],
+    srcs: [":services.musicsearch-sources"],
+    libs: ["services.core", "app-compat-annotations"],
+}
\ No newline at end of file
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
new file mode 100644
index 0000000..e258ef0
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -0,0 +1,300 @@
+/*
+ * 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.server.musicrecognition;
+
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_KILLED;
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
+import static android.media.musicrecognition.MusicRecognitionManager.RecognitionFailureCode;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.media.AudioRecord;
+import android.media.MediaMetadata;
+import android.media.musicrecognition.IMusicRecognitionManagerCallback;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Handles per-user requests received by
+ * {@link MusicRecognitionManagerService}. Opens an audio stream from the
+ * dsp and writes it into a pipe to {@link RemoteMusicRecognitionService}.
+ */
+public final class MusicRecognitionManagerPerUserService extends
+        AbstractPerUserSystemService<MusicRecognitionManagerPerUserService,
+                MusicRecognitionManagerService>
+        implements RemoteMusicRecognitionService.Callbacks {
+
+    private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName();
+    // Number of bytes per sample of audio (which is a short).
+    private static final int BYTES_PER_SAMPLE = 2;
+    private static final int MAX_STREAMING_SECONDS = 24;
+
+    @Nullable
+    @GuardedBy("mLock")
+    private RemoteMusicRecognitionService mRemoteService;
+
+    private MusicRecognitionServiceCallback mRemoteServiceCallback =
+            new MusicRecognitionServiceCallback();
+    private IMusicRecognitionManagerCallback mCallback;
+
+    MusicRecognitionManagerPerUserService(
+            @NonNull MusicRecognitionManagerService primary,
+            @NonNull Object lock, int userId) {
+        super(primary, lock, userId);
+    }
+
+    @NonNull
+    @GuardedBy("mLock")
+    @Override
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        ServiceInfo si;
+        try {
+            si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+        if (!Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE.equals(si.permission)) {
+            Slog.w(TAG, "MusicRecognitionService from '" + si.packageName
+                    + "' does not require permission "
+                    + Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE);
+            throw new SecurityException("Service does not require permission "
+                    + Manifest.permission.BIND_MUSIC_RECOGNITION_SERVICE);
+        }
+        // TODO(b/158194857): check process which owns the service has RECORD_AUDIO permission. How?
+        return si;
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteMusicRecognitionService ensureRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteMusicRecognitionService(getContext(),
+                    serviceComponent, mUserId, this,
+                    mRemoteServiceCallback, mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
+        return mRemoteService;
+    }
+
+    /**
+     * Read audio from the given capture session using an AudioRecord and writes it to a
+     * ParcelFileDescriptor.
+     */
+    @GuardedBy("mLock")
+    public void beginRecognitionLocked(
+            @NonNull RecognitionRequest recognitionRequest,
+            @NonNull IBinder callback) {
+        int maxAudioLengthSeconds = Math.min(recognitionRequest.getMaxAudioLengthSeconds(),
+                MAX_STREAMING_SECONDS);
+        mCallback = IMusicRecognitionManagerCallback.Stub.asInterface(callback);
+        AudioRecord audioRecord = createAudioRecord(recognitionRequest, maxAudioLengthSeconds);
+
+        mRemoteService = ensureRemoteServiceLocked();
+        if (mRemoteService == null) {
+            try {
+                mCallback.onRecognitionFailed(
+                        RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
+            } catch (RemoteException e) {
+                // Ignored.
+            }
+            return;
+        }
+
+        Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
+        if (clientPipe == null) {
+            try {
+                mCallback.onAudioStreamClosed();
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+            return;
+        }
+        ParcelFileDescriptor audioSink = clientPipe.second;
+        ParcelFileDescriptor clientRead = clientPipe.first;
+
+        mMaster.mExecutorService.execute(() -> {
+            try (OutputStream fos =
+                        new ParcelFileDescriptor.AutoCloseOutputStream(audioSink)) {
+                int halfSecondBufferSize =
+                        audioRecord.getBufferSizeInFrames() / maxAudioLengthSeconds;
+                byte[] byteBuffer = new byte[halfSecondBufferSize];
+                int bytesRead = 0;
+                int totalBytesRead = 0;
+                int ignoreBytes =
+                        recognitionRequest.getIgnoreBeginningFrames() * BYTES_PER_SAMPLE;
+                audioRecord.startRecording();
+                while (bytesRead >= 0 && totalBytesRead
+                        < audioRecord.getBufferSizeInFrames() * BYTES_PER_SAMPLE) {
+                    bytesRead = audioRecord.read(byteBuffer, 0, byteBuffer.length);
+                    if (bytesRead > 0) {
+                        totalBytesRead += bytesRead;
+                        // If we are ignoring the first x bytes, update that counter.
+                        if (ignoreBytes > 0) {
+                            ignoreBytes -= bytesRead;
+                            // If we've dipped negative, we've skipped through all ignored bytes
+                            // and then some.  Write out the bytes we shouldn't have skipped.
+                            if (ignoreBytes < 0) {
+                                fos.write(byteBuffer, bytesRead + ignoreBytes, -ignoreBytes);
+                            }
+                        } else {
+                            fos.write(byteBuffer);
+                        }
+                    }
+                }
+                Slog.i(TAG, String.format("Streamed %s bytes from audio record", totalBytesRead));
+            } catch (IOException e) {
+                Slog.e(TAG, "Audio streaming stopped.", e);
+            } finally {
+                audioRecord.release();
+                try {
+                    mCallback.onAudioStreamClosed();
+                } catch (RemoteException ignored) {
+                    // Ignored.
+                }
+
+            }
+        });
+        // Send the pipe down to the lookup service while we write to it asynchronously.
+        mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
+    }
+
+    /**
+     * Callback invoked by {@link android.service.musicrecognition.MusicRecognitionService} to pass
+     * back the music search result.
+     */
+    private final class MusicRecognitionServiceCallback extends
+            IMusicRecognitionServiceCallback.Stub {
+        @Override
+        public void onRecognitionSucceeded(MediaMetadata result, Bundle extras) {
+            try {
+                sanitizeBundle(extras);
+                mCallback.onRecognitionSucceeded(result, extras);
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+        }
+
+        @Override
+        public void onRecognitionFailed(@RecognitionFailureCode int failureCode) {
+            try {
+                mCallback.onRecognitionFailed(failureCode);
+            } catch (RemoteException ignored) {
+                // Ignored.
+            }
+        }
+    }
+
+    @Override
+    public void onServiceDied(@NonNull RemoteMusicRecognitionService service) {
+        try {
+            mCallback.onRecognitionFailed(RECOGNITION_FAILED_SERVICE_KILLED);
+        } catch (RemoteException e) {
+            // Ignored.
+        }
+        Slog.w(TAG, "remote service died: " + service);
+    }
+
+    /** Establishes an audio stream from the DSP audio source. */
+    private static AudioRecord createAudioRecord(
+            @NonNull RecognitionRequest recognitionRequest,
+            int maxAudioLengthSeconds) {
+        int sampleRate = recognitionRequest.getAudioFormat().getSampleRate();
+        int bufferSize = getBufferSizeInBytes(sampleRate, maxAudioLengthSeconds);
+        return new AudioRecord(recognitionRequest.getAudioAttributes(),
+                recognitionRequest.getAudioFormat(), bufferSize,
+                recognitionRequest.getCaptureSession());
+    }
+
+    /**
+     * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at
+     * {@code sampleRate} Hz, using the format returned by DSP audio capture.
+     */
+    private static int getBufferSizeInBytes(int sampleRate, int bufferLengthSeconds) {
+        return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds;
+    }
+
+    private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
+        ParcelFileDescriptor[] fileDescriptors;
+        try {
+            fileDescriptors = ParcelFileDescriptor.createPipe();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to create audio stream pipe", e);
+            return null;
+        }
+
+        if (fileDescriptors.length != 2) {
+            Slog.e(TAG, "Failed to create audio stream pipe, "
+                    + "unexpected number of file descriptors");
+            return null;
+        }
+
+        if (!fileDescriptors[0].getFileDescriptor().valid()
+                || !fileDescriptors[1].getFileDescriptor().valid()) {
+            Slog.e(TAG, "Failed to create audio stream pipe, didn't "
+                    + "receive a pair of valid file descriptors.");
+            return null;
+        }
+
+        return Pair.create(fileDescriptors[0], fileDescriptors[1]);
+    }
+
+    /** Removes remote objects from the bundle. */
+    private static void sanitizeBundle(@Nullable Bundle bundle) {
+        if (bundle == null) {
+            return;
+        }
+
+        for (String key : bundle.keySet()) {
+            Object o = bundle.get(key);
+
+            if (o instanceof Bundle) {
+                sanitizeBundle((Bundle) o);
+            } else if (o instanceof IBinder || o instanceof ParcelFileDescriptor) {
+                bundle.remove(key);
+            }
+        }
+    }
+}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
new file mode 100644
index 0000000..b4cb337
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerService.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.musicrecognition;
+
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.media.musicrecognition.MusicRecognitionManager.RECOGNITION_FAILED_SERVICE_UNAVAILABLE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.musicrecognition.IMusicRecognitionManager;
+import android.media.musicrecognition.IMusicRecognitionManagerCallback;
+import android.media.musicrecognition.RecognitionRequest;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Service which allows a DSP audio event to be securely streamed to a designated {@link
+ * MusicRecognitionService}.
+ */
+public class MusicRecognitionManagerService extends
+        AbstractMasterSystemService<MusicRecognitionManagerService,
+                MusicRecognitionManagerPerUserService> {
+
+    private static final String TAG = MusicRecognitionManagerService.class.getSimpleName();
+
+    private MusicRecognitionManagerStub mMusicRecognitionManagerStub;
+    final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+    /**
+     * Initializes the system service.
+     *
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     *
+     * @param context The system server context.
+     */
+    public MusicRecognitionManagerService(@NonNull Context context) {
+        super(context, new FrameworkResourcesServiceNameResolver(context,
+                        com.android.internal.R.string.config_defaultMusicRecognitionService),
+                /** disallowProperty */null);
+    }
+
+    @Nullable
+    @Override
+    protected MusicRecognitionManagerPerUserService newServiceLocked(int resolvedUserId,
+            boolean disabled) {
+        return new MusicRecognitionManagerPerUserService(this, mLock, resolvedUserId);
+    }
+
+    @Override
+    public void onStart() {
+        mMusicRecognitionManagerStub = new MusicRecognitionManagerStub();
+        publishBinderService(Context.MUSIC_RECOGNITION_SERVICE, mMusicRecognitionManagerStub);
+    }
+
+    private void enforceCaller(String func) {
+        Context ctx = getContext();
+        if (ctx.checkCallingPermission(android.Manifest.permission.MANAGE_MUSIC_RECOGNITION)
+                == PERMISSION_GRANTED) {
+            return;
+        }
+
+        String msg = "Permission Denial: " + func + " from pid="
+                + Binder.getCallingPid()
+                + ", uid=" + Binder.getCallingUid()
+                + " doesn't hold " + android.Manifest.permission.MANAGE_MUSIC_RECOGNITION;
+        throw new SecurityException(msg);
+    }
+
+    final class MusicRecognitionManagerStub extends IMusicRecognitionManager.Stub {
+        @Override
+        public void beginRecognition(
+                @NonNull RecognitionRequest recognitionRequest,
+                @NonNull IBinder callback) {
+            enforceCaller("beginRecognition");
+
+            synchronized (mLock) {
+                final MusicRecognitionManagerPerUserService service = getServiceForUserLocked(
+                        UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.beginRecognitionLocked(recognitionRequest, callback);
+                } else {
+                    try {
+                        IMusicRecognitionManagerCallback.Stub.asInterface(callback)
+                                .onRecognitionFailed(RECOGNITION_FAILED_SERVICE_UNAVAILABLE);
+                    } catch (RemoteException e) {
+                        // ignored.
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
new file mode 100644
index 0000000..4814a82
--- /dev/null
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -0,0 +1,84 @@
+/*
+ * 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.server.musicrecognition;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.musicrecognition.IMusicRecognitionService;
+import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.MusicRecognitionService;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+/** Remote connection to an instance of {@link MusicRecognitionService}. */
+public class RemoteMusicRecognitionService extends
+        AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService,
+                IMusicRecognitionService> {
+
+    // Maximum time allotted for the remote service to return a result. Up to 24s of audio plus
+    // time to fingerprint and make rpcs.
+    private static final long TIMEOUT_IDLE_BIND_MILLIS = 40 * DateUtils.SECOND_IN_MILLIS;
+
+    // Allows the remote service to send back a result.
+    private final IMusicRecognitionServiceCallback mServerCallback;
+
+    public RemoteMusicRecognitionService(Context context, ComponentName serviceName,
+            int userId, MusicRecognitionManagerPerUserService perUserService,
+            IMusicRecognitionServiceCallback callback,
+            boolean bindInstantServiceAllowed, boolean verbose) {
+        super(context, MusicRecognitionService.ACTION_MUSIC_SEARCH_LOOKUP, serviceName, userId,
+                perUserService,
+                context.getMainThreadHandler(),
+                // Prevents the service from having its permissions stripped while in background.
+                Context.BIND_INCLUDE_CAPABILITIES | (bindInstantServiceAllowed
+                        ? Context.BIND_ALLOW_INSTANT : 0), verbose,
+                /* initialCapacity= */ 1);
+        mServerCallback = callback;
+    }
+
+    @NonNull
+    @Override
+    protected IMusicRecognitionService getServiceInterface(@NonNull IBinder service) {
+        return IMusicRecognitionService.Stub.asInterface(service);
+    }
+
+    @Override
+    protected long getTimeoutIdleBindMillis() {
+        return TIMEOUT_IDLE_BIND_MILLIS;
+    }
+
+    /**
+     * Required, but empty since we don't need to notify the callback implementation of the request
+     * results.
+     */
+    interface Callbacks extends VultureCallback<RemoteMusicRecognitionService> {}
+
+    /**
+     * Sends the given descriptor to the app's {@link MusicRecognitionService} to read the
+     * audio.
+     */
+    public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd,
+            @NonNull AudioFormat audioFormat) {
+        scheduleAsyncRequest(
+                binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback));
+    }
+}
diff --git a/services/net/java/android/net/TcpKeepalivePacketData.java b/services/net/java/android/net/TcpKeepalivePacketData.java
index c0c386b..4875c7c 100644
--- a/services/net/java/android/net/TcpKeepalivePacketData.java
+++ b/services/net/java/android/net/TcpKeepalivePacketData.java
@@ -19,11 +19,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.util.IpUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.OsConstants;
 
+import com.android.net.module.util.IpUtils;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
diff --git a/services/profcollect/Android.bp b/services/profcollect/Android.bp
index 68fba55..b7be5d4 100644
--- a/services/profcollect/Android.bp
+++ b/services/profcollect/Android.bp
@@ -30,6 +30,7 @@
 
 java_library_static {
   name: "services.profcollect",
+  defaults: ["services_defaults"],
   srcs: [":services.profcollect-sources"],
   libs: ["services.core"],
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 09e3bfe..7d17109 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -41,6 +41,7 @@
 import static com.android.server.backup.testing.Utils.transferStreamedData;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -2880,8 +2881,8 @@
     }
 
     private static IterableSubject assertDirectory(Path directory) throws IOException {
-        return assertThat(oneTimeIterable(Files.newDirectoryStream(directory).iterator()))
-                .named("directory " + directory);
+        return assertWithMessage("directory " + directory).that(
+                oneTimeIterable(Files.newDirectoryStream(directory).iterator()));
     }
 
     private static void assertJournalDoesNotContain(
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
index 3fe1f3f..3114a75 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/TestUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.backup.testing;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.robolectric.Shadows.shadowOf;
 
@@ -95,8 +96,8 @@
      * logcat before that.
      */
     public static void assertLogcatAtMost(String tag, int level) {
-        assertThat(ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
-                .named("All logs <= " + level)
+        assertWithMessage("All logs <= " + level).that(
+                ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
                 .isTrue();
     }
 
@@ -105,8 +106,8 @@
      * logcat before that.
      */
     public static void assertLogcatAtLeast(String tag, int level) {
-        assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
-                .named("Any log >= " + level)
+        assertWithMessage("Any log >= " + level).that(
+                ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
                 .isTrue();
     }
 
@@ -121,11 +122,10 @@
      * that uses logcat before that.
      */
     public static void assertLogcat(String tag, int... logs) {
-        assertThat(
+        assertWithMessage("Log items (specified per level)").that(
                         ShadowLog.getLogsForTag(tag).stream()
                                 .map(logItem -> logItem.type)
                                 .collect(toSet()))
-                .named("Log items (specified per level)")
                 .containsExactly(IntStream.of(logs).boxed().toArray());
     }
 
@@ -135,15 +135,13 @@
 
     /** Declare shadow {@link ShadowEventLog} to use this. */
     public static void assertEventLogged(int tag, Object... values) {
-        assertThat(ShadowEventLog.getEntries())
-                .named("Event logs")
+        assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
                 .contains(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
     }
 
     /** Declare shadow {@link ShadowEventLog} to use this. */
     public static void assertEventNotLogged(int tag, Object... values) {
-        assertThat(ShadowEventLog.getEntries())
-                .named("Event logs")
+        assertWithMessage("Event logs").that(ShadowEventLog.getEntries())
                 .doesNotContain(new ShadowEventLog.Entry(tag, Arrays.asList(values)));
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index 04e8b63..b85da94 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -34,12 +34,12 @@
 import android.os.HandlerThread;
 import android.os.Process;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService.Injector;
 import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerService;
 
@@ -55,7 +55,11 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.quality.Strictness;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
 
 @Presubmit
 public class AppChildProcessTest {
@@ -68,6 +72,7 @@
 
     private Context mContext = getInstrumentation().getTargetContext();
     private TestInjector mInjector;
+    private PhantomTestInjector mPhantomInjector;
     private ActivityManagerService mAms;
     private ProcessList mProcessList;
     private PhantomProcessList mPhantomProcessList;
@@ -94,6 +99,7 @@
         mProcessList = spy(pList);
 
         mInjector = new TestInjector(mContext);
+        mPhantomInjector = new PhantomTestInjector();
         mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
         mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
@@ -101,6 +107,7 @@
         mAms.mPackageManagerInt = mPackageManagerInt;
         pList.mService = mAms;
         mPhantomProcessList = mAms.mPhantomProcessList;
+        mPhantomProcessList.mInjector = mPhantomInjector;
         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
         doReturn(false).when(() -> Process.supportsPidFd());
         // Remove stale instance of PackageManagerInternal if there is any
@@ -136,47 +143,60 @@
         final String child2ProcessName = "test1_child1_child2";
         final String nativeProcessName = "test_native";
 
-        makeParent(zygote64Pid, initPid);
-        makeParent(zygote32Pid, initPid);
+        makeProcess(rootUid, zygote64Pid, zygote64ProcessName);
+        makeParent(rootUid, zygote64Pid, initPid);
+        makeProcess(rootUid, zygote32Pid, zygote32ProcessName);
+        makeParent(rootUid, zygote32Pid, initPid);
 
         makeAppProcess(app1Pid, app1Uid, app1ProcessName, app1ProcessName);
-        makeParent(app1Pid, zygote64Pid);
         makeAppProcess(app2Pid, app2Uid, app2ProcessName, app2ProcessName);
-        makeParent(app2Pid, zygote64Pid);
+
+        mPhantomProcessList.lookForPhantomProcessesLocked();
 
         assertEquals(0, mPhantomProcessList.mPhantomProcesses.size());
 
         // Verify zygote itself isn't a phantom process
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                zygote64ProcessName, rootUid, zygote64Pid));
+                zygote64ProcessName, rootUid, zygote64Pid, false));
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                zygote32ProcessName, rootUid, zygote32Pid));
+                zygote32ProcessName, rootUid, zygote32Pid, false));
         // Verify none of the app isn't a phantom process
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                app1ProcessName, app1Uid, app1Pid));
+                app1ProcessName, app1Uid, app1Pid, false));
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                app2ProcessName, app2Uid, app2Pid));
+                app2ProcessName, app2Uid, app2Pid, false));
 
         // "Fork" an app child process
-        makeParent(child1Pid, app1Pid);
+        makeProcess(app1Uid, child1Pid, child1ProcessName);
+        makeParent(app1Uid, child1Pid, app1Pid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         PhantomProcessRecord pr = mPhantomProcessList
-                .getOrCreatePhantomProcessIfNeededLocked(child1ProcessName, app1Uid, child1Pid);
+                .getOrCreatePhantomProcessIfNeededLocked(
+                        child1ProcessName, app1Uid, child1Pid, true);
         assertTrue(pr != null);
         assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
         assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
         verifyPhantomProcessRecord(pr, child1ProcessName, app1Uid, child1Pid);
 
         // Create another native process from init
-        makeParent(nativePid, initPid);
+        makeProcess(rootUid, nativePid, nativeProcessName);
+        makeParent(rootUid, nativePid, initPid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         assertEquals(null, mPhantomProcessList.getOrCreatePhantomProcessIfNeededLocked(
-                nativeProcessName, rootUid, nativePid));
+                nativeProcessName, rootUid, nativePid, false));
         assertEquals(1, mPhantomProcessList.mPhantomProcesses.size());
         assertEquals(pr, mPhantomProcessList.mPhantomProcesses.valueAt(0));
 
         // "Fork" another app child process
-        makeParent(child2Pid, child1Pid);
+        makeProcess(app1Uid, child2Pid, child2ProcessName);
+        makeParent(app1Uid, child2Pid, app1Pid);
+        mPhantomProcessList.lookForPhantomProcessesLocked();
+
         PhantomProcessRecord pr2 = mPhantomProcessList
-                .getOrCreatePhantomProcessIfNeededLocked(child2ProcessName, app1Uid, child2Pid);
+                .getOrCreatePhantomProcessIfNeededLocked(
+                        child2ProcessName, app1Uid, child2Pid, false);
         assertTrue(pr2 != null);
         assertEquals(2, mPhantomProcessList.mPhantomProcesses.size());
         verifyPhantomProcessRecord(pr2, child2ProcessName, app1Uid, child2Pid);
@@ -197,19 +217,27 @@
         assertEquals(pid, pr.mPid);
     }
 
+    private void makeProcess(int uid, int pid, String processName) {
+        doReturn(uid).when(() -> Process.getUidForPid(eq(pid)));
+        mPhantomInjector.mPidToName.put(pid, processName);
+    }
+
     private void makeAppProcess(int pid, int uid, String packageName, String processName) {
+        makeProcess(uid, pid, processName);
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = packageName;
+        ai.uid = uid;
         ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
         app.pid = pid;
         mAms.mPidsSelfLocked.doAddInternal(app);
+        mPhantomInjector.addToProcess(uid, pid, pid);
     }
 
-    private void makeParent(int pid, int ppid) {
-        doReturn(ppid).when(() -> Process.getParentPid(eq(pid)));
+    private void makeParent(int uid, int pid, int ppid) {
+        mPhantomInjector.addToProcess(uid, ppid, pid);
     }
 
-    private class TestInjector extends Injector {
+    private class TestInjector extends ActivityManagerService.Injector {
         TestInjector(Context context) {
             super(context);
         }
@@ -230,6 +258,56 @@
         }
     }
 
+    private class PhantomTestInjector extends PhantomProcessList.Injector {
+        ArrayMap<String, InputStream> mPathToInput = new ArrayMap<>();
+        ArrayMap<String, StringBuffer> mPathToData = new ArrayMap<>();
+        ArrayMap<InputStream, StringBuffer> mInputToData = new ArrayMap<>();
+        ArrayMap<Integer, String> mPidToName = new ArrayMap<>();
+
+        @Override
+        InputStream openCgroupProcs(String path) throws FileNotFoundException, SecurityException {
+            InputStream input = mPathToInput.get(path);
+            if (input != null) {
+                return input;
+            }
+            input = new ByteArrayInputStream(new byte[8]); // buf size doesn't matter here
+            mPathToInput.put(path, input);
+            StringBuffer sb = mPathToData.get(path);
+            if (sb == null) {
+                sb = new StringBuffer();
+                mPathToData.put(path, sb);
+            }
+            mInputToData.put(input, sb);
+            return input;
+        }
+
+        @Override
+        int readCgroupProcs(InputStream input, byte[] buf, int offset, int len) throws IOException {
+            StringBuffer sb = mInputToData.get(input);
+            if (sb == null) {
+                return -1;
+            }
+            byte[] avail = sb.toString().getBytes();
+            System.arraycopy(avail, 0, buf, offset, Math.min(len, avail.length));
+            return Math.min(len, avail.length);
+        }
+
+        @Override
+        String getProcessName(final int pid) {
+            return mPidToName.get(pid);
+        }
+
+        void addToProcess(int uid, int pid, int newPid) {
+            final String path = PhantomProcessList.getCgroupFilePath(uid, pid);
+            StringBuffer sb = mPathToData.get(path);
+            if (sb == null) {
+                sb = new StringBuffer();
+                mPathToData.put(path, sb);
+            }
+            sb.append(newPid).append('\n');
+        }
+    }
+
     static class ServiceThreadRule implements TestRule {
         private ServiceThread mThread;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
index a0f48c6..d67eddd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationFudgerTest.java
@@ -79,7 +79,7 @@
             Location coarse = mFudger.createCoarse(fine);
 
             assertThat(coarse).isNotNull();
-            assertThat(coarse).isNotSameAs(fine);
+            assertThat(coarse).isNotSameInstanceAs(fine);
             assertThat(coarse.hasBearing()).isFalse();
             assertThat(coarse.hasSpeed()).isFalse();
             assertThat(coarse.hasAltitude()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index dbaa482..9ee1205 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -20,7 +20,6 @@
 
 import static com.android.server.attention.AttentionManagerService.ATTENTION_CACHE_BUFFER_SIZE;
 import static com.android.server.attention.AttentionManagerService.DEFAULT_STALE_AFTER_MILLIS;
-import static com.android.server.attention.AttentionManagerService.DEVICE_CONFIG_MAX_STALENESS_MILLIS;
 import static com.android.server.attention.AttentionManagerService.KEY_STALE_AFTER_MILLIS;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -40,7 +39,6 @@
 import android.os.IThermalService;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.service.attention.IAttentionCallback;
 import android.service.attention.IAttentionService;
@@ -118,7 +116,7 @@
 
     @Test
     public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
-        doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+        mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(false).when(mMockIPowerManager).isInteractive();
         AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
         assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
@@ -126,7 +124,7 @@
 
     @Test
     public void testCheckAttention_callOnSuccess() throws RemoteException {
-        doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+        mSpyAttentionManager.mIsServiceEnabled = true;
         doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
         doReturn(true).when(mMockIPowerManager).isInteractive();
         doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
@@ -207,38 +205,6 @@
                 DEFAULT_STALE_AFTER_MILLIS);
     }
 
-    @Test
-    public void testEnsureDeviceConfigCachedValuesFreshness_doesNotCallDeviceConfigTooFrequently() {
-        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                KEY_STALE_AFTER_MILLIS, String.valueOf(DEFAULT_STALE_AFTER_MILLIS), false);
-        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
-                DEFAULT_STALE_AFTER_MILLIS);
-
-        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                KEY_STALE_AFTER_MILLIS, "123", false);
-
-        // New value is ignored
-        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
-                DEFAULT_STALE_AFTER_MILLIS);
-    }
-
-
-    @Test
-    public void testEnsureDeviceConfigCachedValuesFreshness_refreshesWhenStale() {
-        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                KEY_STALE_AFTER_MILLIS, String.valueOf(DEFAULT_STALE_AFTER_MILLIS), false);
-        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(
-                DEFAULT_STALE_AFTER_MILLIS);
-
-        DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
-                KEY_STALE_AFTER_MILLIS, "123", false);
-        mSpyAttentionManager.mLastReadDeviceConfigMillis =
-                SystemClock.elapsedRealtime() - (DEVICE_CONFIG_MAX_STALENESS_MILLIS + 1);
-
-        // Values are refreshed
-        assertThat(mSpyAttentionManager.getStaleAfterMillis()).isEqualTo(123);
-    }
-
     private class MockIAttentionService implements IAttentionService {
         public void checkAttention(IAttentionCallback callback) throws RemoteException {
             callback.onSuccess(0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
new file mode 100644
index 0000000..84987e6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/HardwareAuthTokenUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.keymaster.HardwareAuthToken;
+import android.hardware.keymaster.Timestamp;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class HardwareAuthTokenUtilsTest {
+
+    @Test
+    public void testByteArrayLoopBack() {
+        final byte[] hat = new byte[69];
+        for (int i = 0; i < 69; i++) {
+            hat[i] = (byte) i;
+        }
+
+        final HardwareAuthToken hardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+        final byte[] hat2 = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+
+        for (int i = 0; i < hat.length; i++) {
+            assertEquals(hat[i], hat2[i]);
+        }
+    }
+
+    @Test
+    public void testHardwareAuthTokenLoopBack() {
+        final long testChallenge = 1000L;
+        final long testUserId = 2000L;
+        final long testAuthenticatorId = 3000L;
+        final int testAuthenticatorType = 4000;
+        final long testTimestamp = 5000L;
+
+        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+        hardwareAuthToken.challenge = testChallenge;
+        hardwareAuthToken.userId = testUserId;
+        hardwareAuthToken.authenticatorId = testAuthenticatorId;
+        hardwareAuthToken.authenticatorType = testAuthenticatorType;
+        hardwareAuthToken.timestamp = new Timestamp();
+        hardwareAuthToken.timestamp.milliSeconds = testTimestamp;
+        hardwareAuthToken.mac = new byte[32];
+
+        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+            hardwareAuthToken.mac[i] = (byte) i;
+        }
+
+        final byte[] hat = HardwareAuthTokenUtils.toByteArray(hardwareAuthToken);
+        final HardwareAuthToken hardwareAuthToken2 =
+                HardwareAuthTokenUtils.toHardwareAuthToken(hat);
+
+        assertEquals(testChallenge, hardwareAuthToken2.challenge);
+        assertEquals(testUserId, hardwareAuthToken2.userId);
+        assertEquals(testAuthenticatorId, hardwareAuthToken2.authenticatorId);
+        assertEquals(testAuthenticatorType, hardwareAuthToken2.authenticatorType);
+        assertEquals(testTimestamp, hardwareAuthToken2.timestamp.milliSeconds);
+
+        for (int i = 0; i < hardwareAuthToken.mac.length; i++) {
+            assertEquals(hardwareAuthToken.mac[i], hardwareAuthToken2.mac[i]);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 7cbf571..ef98b98 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -814,7 +814,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -834,7 +834,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -853,7 +853,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
@@ -872,7 +872,7 @@
         mTestLooper.dispatchAll();
 
         assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+        assertThat(mNativeWrapper.getResultMessages()).containsAtLeast(pressed, released);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
index cdff97b..9ab762a 100644
--- a/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/MultiClientInputMethodManagerServiceTest.java
@@ -76,7 +76,7 @@
 
         // Act and assert
         assertThat(MultiClientInputMethodManagerService.resolveMultiClientImeService(
-                asList(imeService))).isSameAs(imeService);
+                asList(imeService))).isSameInstanceAs(imeService);
     }
 
     private ResolveInfo buildResolveInfo(String permission, int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 41be54a..f26e094 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -194,7 +194,7 @@
         assertThat(rulesFetched.size())
                 .isEqualTo(INDEXING_BLOCK_SIZE * 2 + unindexedRuleCount);
         assertThat(rulesFetched)
-                .containsAllOf(
+                .containsAtLeast(
                         getPackageNameIndexedRule(installedPackageName),
                         getAppCertificateIndexedRule(installedAppCertificate));
     }
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
index 192ade7..4810563 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
@@ -45,6 +45,7 @@
         return new ConfigurationInternal.Builder(userId)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(geoDetectionEnabled)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
index 6921bb2..8d5687c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -71,7 +71,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
+        assertThat(filteredKeys.entrySet()).containsAtLeastElementsIn(rawKeys.entrySet());
     }
 
     @Test
@@ -85,7 +85,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
@@ -100,7 +100,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
@@ -122,7 +122,7 @@
         Map<String, Pair<SecretKey, byte[]>> filteredKeys =
                 mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
         assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
-        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+        assertThat(rawKeys.entrySet()).containsAtLeastElementsIn(filteredKeys.entrySet());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
index 9836c64..b0cb2ea 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java
@@ -70,7 +70,7 @@
         CertXml certXml = CertXml.parse(certXmlBytes);
         List<X509Certificate> endpointCerts = certXml.getAllEndpointCerts();
         assertThat(endpointCerts).hasSize(3);
-        assertThat(endpointCerts).containsAllOf(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
+        assertThat(endpointCerts).containsAtLeast(TestData.LEAF_CERT_1, TestData.LEAF_CERT_2);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 154d42c..3a292de 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -72,7 +72,8 @@
         @JvmStatic
         fun checkAllCasesHandled() {
             // Assert that all states have been tested at least once.
-            assertThat(CASES.map { it.state }.distinct()).containsAllIn(ActorState.values())
+            assertThat(CASES.map { it.state }.distinct())
+                    .containsAtLeastElementsIn(ActorState.values())
         }
 
         @BeforeClass
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 44bb58f..22b0715 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -639,7 +639,7 @@
         UserInfo user1 = createUser("User 1", 0);
         UserInfo user2 = createUser("User 2", 0);
         long[] serialNumbersOfUsers = mUserManager.getSerialNumbersOfUsers(false);
-        assertThat(serialNumbersOfUsers).asList().containsAllOf(
+        assertThat(serialNumbersOfUsers).asList().containsAtLeast(
                 (long) user1.serialNumber, (long) user2.serialNumber);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index b6a0979..eedc978 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -46,41 +46,25 @@
     private static final String INSTALLER = "some.installer";
 
     private static final Correspondence<VersionedPackage, VersionedPackage> VER_PKG_CORR =
-            new Correspondence<VersionedPackage, VersionedPackage>() {
-                @Override
-                public boolean compare(VersionedPackage a, VersionedPackage b) {
-                    if (a == null || b == null) {
-                        return a == b;
-                    }
-                    return a.equals(b);
+            Correspondence.from((VersionedPackage a, VersionedPackage b) -> {
+                if (a == null || b == null) {
+                    return a == b;
                 }
-
-                @Override
-                public String toString() {
-                    return "is the same as";
-                }
-            };
+                return a.equals(b);
+            }, "is the same as");
 
     private static final Correspondence<PackageRollbackInfo.RestoreInfo,
             PackageRollbackInfo.RestoreInfo>
             RESTORE_INFO_CORR =
-            new Correspondence<PackageRollbackInfo.RestoreInfo, PackageRollbackInfo.RestoreInfo>() {
-                @Override
-                public boolean compare(PackageRollbackInfo.RestoreInfo a,
-                        PackageRollbackInfo.RestoreInfo b) {
-                    if (a == null || b == null) {
-                        return a == b;
-                    }
-                    return a.userId == b.userId
-                            && a.appId == b.appId
-                            && Objects.equals(a.seInfo, b.seInfo);
+            Correspondence.from((PackageRollbackInfo.RestoreInfo a,
+                    PackageRollbackInfo.RestoreInfo b) -> {
+                if (a == null || b == null) {
+                    return a == b;
                 }
-
-                @Override
-                public String toString() {
-                    return "is the same as";
-                }
-            };
+                return a.userId == b.userId
+                        && a.appId == b.appId
+                        && Objects.equals(a.seInfo, b.seInfo);
+            }, "is the same as");
 
     private static final String JSON_ROLLBACK_NO_EXT = "{'info':{'rollbackId':123,'packages':"
             + "[{'versionRolledBackFrom':{'packageName':'blah','longVersionCode':55},"
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
index 46224cb..8fb2e68 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsFileLoggerTest.java
@@ -132,7 +132,7 @@
                     appSizes.getLong(i), cacheSizes.getLong(i));
             apps.add(app);
         }
-        assertThat(apps).containsAllOf(new AppSizeGrouping("com.test.app", 1100, 20),
+        assertThat(apps).containsAtLeast(new AppSizeGrouping("com.test.app", 1100, 20),
                 new AppSizeGrouping("com.test.app2", 11, 2));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
index 54b5bee..682a80c 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -47,6 +47,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -91,7 +92,7 @@
             TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
             assertEquals(CAPABILITY_POSSESSED,
                     capabilities.getConfigureAutoDetectionEnabledCapability());
-            assertEquals(CAPABILITY_POSSESSED,
+            assertEquals(CAPABILITY_NOT_APPLICABLE,
                     capabilities.getConfigureGeoDetectionEnabledCapability());
             assertEquals(CAPABILITY_POSSESSED,
                     capabilities.getSuggestManualTimeZoneCapability());
@@ -108,6 +109,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(false)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -169,6 +171,7 @@
         ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionSupported(false)
+                .setGeoDetectionSupported(false)
                 .setAutoDetectionEnabled(true)
                 .setLocationEnabled(true)
                 .setGeoDetectionEnabled(true)
@@ -220,4 +223,67 @@
             assertTrue(configuration.isGeoDetectionEnabled());
         }
     }
+
+    /**
+     * Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is true, but
+     * {@link ConfigurationInternal#isGeoDetectionSupported()} is false.
+     */
+    @Test
+    public void test_geoDetectNotSupported() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(false)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                    autoOnConfig.createCapabilitiesAndConfig();
+
+            TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+            assertEquals(CAPABILITY_POSSESSED,
+                    capabilities.getConfigureAutoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_SUPPORTED,
+                    capabilities.getConfigureGeoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_APPLICABLE,
+                    capabilities.getSuggestManualTimeZoneCapability());
+
+            TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+            assertTrue(configuration.isAutoDetectionEnabled());
+            assertTrue(configuration.isGeoDetectionEnabled());
+        }
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
+                    autoOffConfig.createCapabilitiesAndConfig();
+
+            TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities();
+            assertEquals(CAPABILITY_POSSESSED,
+                    capabilities.getConfigureAutoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_NOT_SUPPORTED,
+                    capabilities.getConfigureGeoDetectionEnabledCapability());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZoneCapability());
+
+            TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
+            assertFalse(configuration.isAutoDetectionEnabled());
+            assertTrue(configuration.isGeoDetectionEnabled());
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index cb27657..d2452ea 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -365,6 +365,7 @@
         final boolean geoDetectionEnabled = autoDetectionEnabled;
         return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
                 .setAutoDetectionSupported(true)
+                .setGeoDetectionSupported(true)
                 .setUserConfigAllowed(true)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
                 .setLocationEnabled(geoDetectionEnabled)
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 296aa73..a6ffd20 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -91,6 +91,7 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
@@ -100,6 +101,7 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(false)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
@@ -109,32 +111,36 @@
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionSupported(false)
+                    .setGeoDetectionSupported(false)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
+    private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(false)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
     private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
                     .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
-    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED =
-            new ConfigurationInternal.Builder(USER_ID)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionSupported(true)
-                    .setAutoDetectionEnabled(false)
-                    .setLocationEnabled(true)
-                    .setGeoDetectionEnabled(true)
-                    .build();
-
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -144,6 +150,7 @@
     private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
             new ConfigurationInternal.Builder(USER_ID)
                     .setAutoDetectionSupported(true)
+                    .setGeoDetectionSupported(true)
                     .setUserConfigAllowed(true)
                     .setAutoDetectionEnabled(true)
                     .setLocationEnabled(true)
@@ -223,14 +230,14 @@
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration with auto detection enabled.
+        // Try to update the configuration with auto detection enabled.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_AUTO_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Try to  update the configuration to enable geolocation time zone detection.
+        // Try to update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
 
@@ -249,7 +256,7 @@
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration with auto detection enabled.
+        // Try to update the configuration with auto detection enabled.
         script.simulateUpdateConfiguration(
                 USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
 
@@ -258,6 +265,38 @@
     }
 
     @Test
+    public void testUpdateConfiguration_autoDetectSupportedGeoNotSupported() {
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED);
+
+        // Update the configuration with auto detection disabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
+
+        // The settings should have been changed and the StrategyListener onChange() called.
+        ConfigurationInternal expectedConfig =
+                new ConfigurationInternal.Builder(CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED)
+                        .setAutoDetectionEnabled(false)
+                        .build();
+        script.verifyConfigurationChangedAndReset(expectedConfig);
+
+        // Try to update the configuration with geo detection disabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_GEO_DETECTION_DISABLED, false /* expectedResult */);
+
+        // The settings should not have been changed: user shouldn't have the capability to modify
+        // the setting when the feature is disabled.
+        script.verifyConfigurationNotChanged();
+
+        // Try to update the configuration with geo detection enabled.
+        script.simulateUpdateConfiguration(
+                USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */);
+
+        // The settings should not have been changed: user shouldn't have the capability to modify
+        // the setting when the feature is disabled.
+        script.verifyConfigurationNotChanged();
+    }
+
+    @Test
     public void testEmptyTelephonySuggestions() {
         TelephonyTimeZoneSuggestion slotIndex1TimeZoneSuggestion =
                 createEmptySlotIndex1Suggestion();
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 7b07102..1055069 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -81,9 +81,7 @@
 
     // A correspondence to compare a FrontendResource and a TunerFrontendInfo.
     private static final Correspondence<FrontendResource, TunerFrontendInfo> FR_TFI_COMPARE =
-            new Correspondence<FrontendResource, TunerFrontendInfo>() {
-            @Override
-            public boolean compare(FrontendResource actual, TunerFrontendInfo expected) {
+            Correspondence.from((FrontendResource actual, TunerFrontendInfo expected) -> {
                 if (actual == null || expected == null) {
                     return (actual == null) && (expected == null);
                 }
@@ -91,13 +89,7 @@
                 return actual.getId() == expected.getId()
                         && actual.getType() == expected.getFrontendType()
                         && actual.getExclusiveGroupId() == expected.getExclusiveGroupId();
-            }
-
-            @Override
-            public String toString() {
-                return "is correctly configured from ";
-            }
-        };
+            },  "is correctly configured from ");
 
     @Before
     public void setUp() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index d7e431f..e304083 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -690,6 +690,39 @@
     }
 
     @Test
+    public void unbindOtherUserServices() throws PackageManager.NameNotFoundException {
+        Context context = mock(Context.class);
+        PackageManager pm = mock(PackageManager.class);
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+
+        when(context.getPackageName()).thenReturn(mContext.getPackageName());
+        when(context.getUserId()).thenReturn(mContext.getUserId());
+        when(context.getPackageManager()).thenReturn(pm);
+        when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai);
+
+        ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm,
+                APPROVAL_BY_COMPONENT);
+        ComponentName cn = ComponentName.unflattenFromString("a/a");
+
+        when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> {
+            Object[] args = invocation.getArguments();
+            ServiceConnection sc = (ServiceConnection) args[1];
+            sc.onServiceConnected(cn, mock(IBinder.class));
+            return true;
+        });
+
+        service.registerService(cn, 0);
+        service.registerService(cn, 10);
+        service.registerService(cn, 11);
+        service.unbindOtherUserServices(11);
+
+        assertFalse(service.isBound(cn, 0));
+        assertFalse(service.isBound(cn, 10));
+        assertTrue(service.isBound(cn, 11));
+    }
+
+    @Test
     public void testPackageUninstall_packageNoLongerInApprovedList() throws Exception {
         for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
             ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ed6a20b..6ee5831 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -43,6 +43,9 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_MUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -110,6 +113,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IIntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -248,6 +252,11 @@
     Resources mResources;
     @Mock
     RankingHandler mRankingHandler;
+    @Mock
+    ActivityManagerInternal mAmi;
+
+    @Mock
+    IIntentSender pi1;
 
     private static final int MAX_POST_DELAY = 1000;
 
@@ -392,7 +401,6 @@
 
         DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
         when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
-        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
@@ -403,7 +411,7 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+        LocalServices.addService(ActivityManagerInternal.class, mAmi);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -477,7 +485,7 @@
                 mGroupHelper, mAm, mAtm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                 mAppOpsManager, mUm, mHistoryManager, mStatsManager,
-                mock(TelephonyManager.class));
+                mock(TelephonyManager.class), mAmi);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
@@ -674,7 +682,8 @@
         }
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
                 .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
         if (extender != null) {
             nb.extend(extender);
         }
@@ -810,6 +819,7 @@
         PendingIntent pendingIntent = mock(PendingIntent.class);
         Intent intent = mock(Intent.class);
         when(pendingIntent.getIntent()).thenReturn(intent);
+        when(pendingIntent.getTarget()).thenReturn(pi1);
 
         ActivityInfo info = new ActivityInfo();
         info.resizeMode = RESIZE_MODE_RESIZEABLE;
@@ -7134,4 +7144,159 @@
         inOrder.verify(parent).recordDismissalSentiment(anyInt());
         inOrder.verify(child).recordDismissalSentiment(anyInt());
     }
+
+    @Test
+    public void testImmutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testImmutableBubbleIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a bubble with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableBubbleIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(pi1))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(true,
+                mTestNotificationChannel, 7, "testMutableBubbleIntent", null, false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testImmutableDirectReplyActionIntent", null, false);
+        try {
+            mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                    r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+            waitForIdle();
+            fail("Allowed a direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateMessageBubbleNotifRecord(false,
+                mTestNotificationChannel, 7, "testMutableDirectReplyActionIntent", null, false);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        try {
+            mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+            fail("Allowed a contextual direct reply with an immutable intent to be posted");
+        } catch (IllegalArgumentException e) {
+            // good
+        }
+    }
+
+    @Test
+    public void testMutableDirectReplyContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        extraAction.add(replyAction);
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                r.getSbn().getTag(), r,false);
+    }
+
+    @Test
+    public void testImmutableActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, r.getSbn().getTag(),
+                r.getSbn().getId(), r.getNotification(), r.getSbn().getUserId());
+
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(r.getSbn().getPackageName());
+        assertEquals(1, notifs.length);
+    }
+
+    @Test
+    public void testImmutableContextualActionIntent() throws Exception {
+        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
+                .thenReturn(FLAG_IMMUTABLE | FLAG_ONE_SHOT);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        ArrayList<Notification.Action> extraAction = new ArrayList<>();
+        extraAction.add(new Notification.Action(0, "hello", null));
+        Bundle signals = new Bundle();
+        signals.putParcelableArrayList(Adjustment.KEY_CONTEXTUAL_ACTIONS, extraAction);
+        Adjustment adjustment = new Adjustment(r.getSbn().getPackageName(), r.getKey(), signals, "",
+                r.getUser());
+        r.addAdjustment(adjustment);
+        r.applyAdjustments();
+
+        mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(),
+                    r.getSbn().getTag(), r,false);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 3281c3f..a80f62a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
@@ -154,7 +155,8 @@
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
                     mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
-                    mock(StatsManager.class), mock(TelephonyManager.class));
+                    mock(StatsManager.class), mock(TelephonyManager.class),
+                    mock(ActivityManagerInternal.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
index eca71b6..e5ae2d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ShortcutHelperTest.java
@@ -305,6 +305,7 @@
         //when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
          //       anyString(), anyInt(), any())).thenReturn(true);
 
-        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM)).isSameAs(si);
+        assertThat(mShortcutHelper.getValidShortcutInfo("a", "p", UserHandle.SYSTEM))
+                .isSameInstanceAs(si);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index e17601e..f88530c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -23,6 +23,8 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 
+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.DisplayArea.Type.ABOVE_TASKS;
 import static com.android.server.wm.DisplayArea.Type.ANY;
@@ -37,14 +39,18 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
 
 import com.google.android.collect.Lists;
 
@@ -405,6 +411,33 @@
                 childBounds1, windowToken.getMaxBounds());
     }
 
+    @Test
+    public void testGetOrientation() {
+        final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test");
+        final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
+        spyOn(token);
+        doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
+        doNothing().when(token).setParent(any());
+        final WindowState win = createWindowState(token);
+        spyOn(win);
+        doNothing().when(win).setParent(any());
+        win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        token.addChild(win, 0);
+        area.addChild(token);
+
+        doReturn(true).when(win).isVisible();
+
+        assertEquals("Visible window can request orientation",
+                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
+                area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+
+        doReturn(false).when(win).isVisible();
+
+        assertEquals("Invisible window cannot request orientation",
+                ActivityInfo.SCREEN_ORIENTATION_NOSENSOR,
+                area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
+    }
+
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
         private TestDisplayArea(WindowManagerService wms, Rect bounds) {
             super(wms, ANY, "half display area");
@@ -417,6 +450,13 @@
         }
     }
 
+    private WindowState createWindowState(WindowToken token) {
+        return new WindowState(mWms, mock(Session.class), new TestIWindow(), token,
+                null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
+                View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
+                false /* ownerCanAddInternalSystemWindow */);
+    }
+
     private WindowToken createWindowToken(int type) {
         return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
                 type, false /* persist */, null /* displayContent */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e45b28e..4d0d3b2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1584,6 +1584,28 @@
         assertFalse(publicDc.forceDesktopMode());
     }
 
+    @Test
+    public void testDisplaySettingsReappliedWhenDisplayChanged() {
+        final DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.copyFrom(mDisplayInfo);
+        final DisplayContent dc = createNewDisplay(displayInfo);
+
+        // Generate width/height/density values different from the default of the display.
+        final int forcedWidth = dc.mBaseDisplayWidth + 1;
+        final int forcedHeight = dc.mBaseDisplayHeight + 1;;
+        final int forcedDensity = dc.mBaseDisplayDensity + 1;;
+        // Update the forced size and density in settings and the unique id to simualate a display
+        // remap.
+        dc.mWmService.mDisplayWindowSettings.setForcedSize(dc, forcedWidth, forcedHeight);
+        dc.mWmService.mDisplayWindowSettings.setForcedDensity(dc, forcedDensity, 0 /* userId */);
+        dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
+        // Trigger display changed.
+        dc.onDisplayChanged();
+        // Ensure overridden size and denisty match the most up-to-date values in settings for the
+        // display.
+        verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
index 8fa0cde..150577a 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -124,7 +124,7 @@
     data class InputData<T : Parcelable>(val valid: T, val validCopy: T, val validOther: T) {
         val kls = valid.javaClass
         init {
-            assertThat(valid).isNotSameAs(validCopy)
+            assertThat(valid).isNotSameInstanceAs(validCopy)
             // Don't use isInstanceOf because of phantom warnings in intellij about Class!
             assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
             assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a6d7245..9f16543 100755
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -966,6 +966,32 @@
         /**
          * Gets the verification status for the phone number of an incoming call as identified in
          * ATIS-1000082.
+         * <p>
+         * For incoming calls, the number verification status indicates whether the device was
+         * able to verify the authenticity of the calling number using the STIR process outlined
+         * in ATIS-1000082.  {@link Connection#VERIFICATION_STATUS_NOT_VERIFIED} indicates that
+         * the network was not able to use STIR to verify the caller's number (i.e. nothing is
+         * known regarding the authenticity of the number.
+         * {@link Connection#VERIFICATION_STATUS_PASSED} indicates that the network was able to
+         * use STIR to verify the caller's number.  This indicates that the network has a high
+         * degree of confidence that the incoming call actually originated from the indicated
+         * number.  {@link Connection#VERIFICATION_STATUS_FAILED} indicates that the network's
+         * STIR verification did not pass.  This indicates that the incoming call may not
+         * actually be from the indicated number.  This could occur if, for example, the caller
+         * is using an impersonated phone number.
+         * <p>
+         * A {@link CallScreeningService} can use this information to help determine if an
+         * incoming call is potentially an unwanted call.  A verification status of
+         * {@link Connection#VERIFICATION_STATUS_FAILED} indicates that an incoming call may not
+         * actually be from the number indicated on the call (i.e. impersonated number) and that it
+         * should potentially be blocked.  Likewise,
+         * {@link Connection#VERIFICATION_STATUS_PASSED} can be used as a positive signal to
+         * help clarify that the incoming call is originating from the indicated number and it
+         * is less likely to be an undesirable call.
+         * <p>
+         * An {@link InCallService} can use this information to provide a visual indicator to the
+         * user regarding the verification status of a call and to help identify calls from
+         * potentially impersonated numbers.
          * @return the verification status.
          */
         public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index af5badd..11fae0c 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -727,6 +727,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
@@ -757,6 +758,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
@@ -807,6 +809,8 @@
     field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
     field public static final int KEY_TYPE_EPDG = 1; // 0x1
     field public static final int KEY_TYPE_WLAN = 2; // 0x2
+    field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
+    field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
     field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
     field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
     field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
diff --git a/telephony/java/android/telephony/BinderCacheManager.java b/telephony/java/android/telephony/BinderCacheManager.java
new file mode 100644
index 0000000..0d3e2fe
--- /dev/null
+++ b/telephony/java/android/telephony/BinderCacheManager.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.telephony;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Keeps track of the connection to a Binder node, refreshes the cache if the node dies, and lets
+ * interested parties register listeners on the node to be notified when the node has died via the
+ * registered {@link Runnable}.
+ * @param <T> The IInterface representing the Binder type that this manager will be managing the
+ *           cache of.
+ * @hide
+ */
+public class BinderCacheManager<T extends IInterface> {
+
+    /**
+     * Factory class for creating new IInterfaces in the case that {@link #getBinder()} is
+     * called and there is no active binder available.
+     * @param <T> The IInterface that should be cached and returned to the caller when
+     * {@link #getBinder()} is called until the Binder node dies.
+     */
+    public interface BinderInterfaceFactory<T> {
+        /**
+         * @return A new instance of the Binder node, which will be cached until it dies.
+         */
+        T create();
+    }
+
+    /**
+     * Tracks the cached Binder node as well as the listeners that were associated with that
+     * Binder node during its lifetime. If the Binder node dies, the listeners will be called and
+     * then this tracker will be unlinked and cleaned up.
+     */
+    private class BinderDeathTracker implements IBinder.DeathRecipient {
+
+        private final T mConnection;
+        private final HashMap<Object, Runnable> mListeners = new HashMap<>();
+
+        /**
+         * Create a tracker to cache the Binder node and add the ability to listen for the cached
+         * interface's death.
+         */
+        BinderDeathTracker(@NonNull T connection) {
+            mConnection = connection;
+            try {
+                mConnection.asBinder().linkToDeath(this, 0 /*flags*/);
+            } catch (RemoteException e) {
+                // isAlive will return false.
+            }
+        }
+
+        public boolean addListener(Object key, Runnable r) {
+            synchronized (mListeners) {
+                if (!isAlive()) return false;
+                mListeners.put(key, r);
+                return true;
+            }
+        }
+
+        public void removeListener(Object runnableKey) {
+            synchronized (mListeners) {
+                mListeners.remove(runnableKey);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            ArrayList<Runnable> listeners;
+            synchronized (mListeners) {
+                listeners = new ArrayList<>(mListeners.values());
+                mListeners.clear();
+                try {
+                    mConnection.asBinder().unlinkToDeath(this, 0 /*flags*/);
+                } catch (NoSuchElementException e) {
+                    // No need to worry about this, this means the death recipient was never linked.
+                }
+            }
+            listeners.forEach(Runnable::run);
+        }
+
+        /**
+         * @return The cached Binder.
+         */
+        public T getConnection() {
+            return mConnection;
+        }
+
+        /**
+         * @return true if the cached Binder is alive at the time of calling, false otherwise.
+         */
+        public boolean isAlive() {
+            return mConnection.asBinder().isBinderAlive();
+        }
+    }
+
+    private final BinderInterfaceFactory<T> mBinderInterfaceFactory;
+    private final AtomicReference<BinderDeathTracker> mCachedConnection;
+
+    /**
+     * Create a new instance, which manages a cached IInterface and creates new ones using the
+     * provided factory when the cached IInterface dies.
+     * @param factory The factory used to create new Instances of the cached IInterface when it
+     *                dies.
+     */
+    public BinderCacheManager(BinderInterfaceFactory<T> factory) {
+        mBinderInterfaceFactory = factory;
+        mCachedConnection = new AtomicReference<>();
+    }
+
+    /**
+     * Get the binder node connection and add a Runnable to be run if this Binder dies. Once this
+     * Runnable is run, the Runnable itself is discarded and must be added again.
+     * <p>
+     * Note: There should be no assumptions here as to which Thread this Runnable is called on. If
+     * the Runnable should be called on a specific thread, it should be up to the caller to handle
+     * that in the runnable implementation.
+     * @param runnableKey The Key associated with this runnable so that it can be removed later
+     *                    using {@link #removeRunnable(Object)} if needed.
+     * @param deadRunnable The runnable that will be run if the cached Binder node dies.
+     * @return T if the runnable was added or {@code null} if the connection is not alive right now
+     * and the associated runnable was never added.
+     */
+    public T listenOnBinder(Object runnableKey, Runnable deadRunnable) {
+        if (runnableKey == null || deadRunnable == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+
+        boolean addSucceeded = tracker.addListener(runnableKey, deadRunnable);
+        return addSucceeded ? tracker.getConnection() : null;
+    }
+
+    /**
+     * @return The cached Binder node. May return null if the requested Binder node is not currently
+     * available.
+     */
+    public T getBinder() {
+        BinderDeathTracker tracker = getTracker();
+        return (tracker != null) ? tracker.getConnection() : null;
+    }
+
+    /**
+     * Removes a previously registered runnable associated with the returned  cached Binder node
+     * using the key it was registered with in {@link #listenOnBinder} if the runnable still exists.
+     * @param runnableKey The key that was used to register the Runnable earlier.
+     * @return The cached Binder node that the runnable used to registered to or null if the cached
+     * Binder node is not alive anymore.
+     */
+    public T removeRunnable(Object runnableKey) {
+        if (runnableKey == null) return null;
+        BinderDeathTracker tracker = getTracker();
+        if (tracker == null) return null;
+        tracker.removeListener(runnableKey);
+        return tracker.getConnection();
+    }
+
+    /**
+     * @return The BinderDeathTracker container, which contains the cached IInterface instance or
+     * null if it is not available right now.
+     */
+    private BinderDeathTracker getTracker() {
+        return mCachedConnection.updateAndGet((oldVal) -> {
+            BinderDeathTracker tracker = oldVal;
+            // Update cache if no longer alive. BinderDied will eventually be called on the tracker,
+            // which will call listeners & clean up.
+            if (tracker == null || !tracker.isAlive()) {
+                T binder = mBinderInterfaceFactory.create();
+                tracker = (binder != null) ? new BinderDeathTracker(binder) : null;
+
+            }
+            return (tracker != null && tracker.isAlive()) ? tracker : null;
+        });
+    }
+
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 12e56cc..c67e8ef 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -10397,19 +10397,25 @@
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
      * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             Manifest.permission.READ_PHONE_STATE,
             Manifest.permission.ACCESS_COARSE_LOCATION
     })
-    public ServiceState getServiceState() {
+    public @Nullable ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
     }
 
     /**
      * Returns the service state information on specified subscription. Callers require
      * either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
+     *
+     * May return {@code null} when the subscription is inactive or when there was an error
+     * communicating with the phone process.
      * @hide
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -10436,9 +10442,9 @@
      * @param accountHandle The handle for the {@link PhoneAccount} for which to retrieve the
      * voicemail ringtone.
      * @return The URI for the ringtone to play when receiving a voicemail from a specific
-     * PhoneAccount.
+     * PhoneAccount. May be {@code null} if no ringtone is set.
      */
-    public Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
+    public @Nullable Uri getVoicemailRingtoneUri(PhoneAccountHandle accountHandle) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
@@ -12808,7 +12814,7 @@
      *  1) User data is turned on, or
      *  2) APN is un-metered for this subscription, or
      *  3) APN type is whitelisted. E.g. MMS is whitelisted if
-     *  {@link #setAlwaysAllowMmsData(boolean)} is turned on.
+     *  {@link #MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED} is enabled.
      *
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
@@ -13285,26 +13291,66 @@
     }
 
     /**
-     * Set allowing mobile data during voice call. This is used for allowing data on the non-default
-     * data SIM. When a voice call is placed on the non-default data SIM on DSDS devices, users will
-     * not be able to use mobile data. By calling this API, data will be temporarily enabled on the
-     * non-default data SIM during the life cycle of the voice call.
+     * Controls whether mobile data  on the non-default SIM is allowed during a voice call.
      *
-     * @param allow {@code true} if allowing using data during voice call, {@code false} if
-     * disallowed.
+     * This is used for allowing data on the non-default data SIM when a voice call is placed on
+     * the non-default data SIM on DSDS devices. If this policy is disabled, users will not be able
+     * to use mobile data via the non-default data SIM during the call, which may mean no mobile
+     * data at all since some modem implementations disallow mobile data via the default data SIM
+     * during voice calls.
+     * If this policy is enabled, data will be temporarily enabled on the non-default data SIM
+     * during any voice calls.
      *
-     * @return {@code true} if operation is successful. otherwise {@code false}.
-     *
-     * @throws SecurityException if the caller doesn't have the permission.
-     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
      * @hide
      */
+    @SystemApi
+    @TestApi
+    public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1;
+
+    /**
+     * Controls whether MMS messages bypass the user-specified "mobile data" toggle.
+     *
+     * When enabled, requests for connections to the MMS APN will be accepted by telephony even if
+     * the user has turned "mobile data" off on this specific sim card. {@link #isDataEnabledForApn}
+     * will also return true for {@link ApnSetting#TYPE_MMS}.
+     * When disabled, the MMS APN will be governed by the same rules as all other APNs.
+     *
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "MOBILE_DATA_POLICY_" }, value = {
+            MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL,
+            MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MobileDataPolicy { }
+
+    /**
+     * Enables or disables a piece of mobile data policy.
+     *
+     * Enables or disables the mobile data policy specified in {@code policy}. See the detailed
+     * description of each policy constant for what they do.
+     *
+     * @param policy The data policy to enable.
+     * @param enabled Whether to enable or disable the policy.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setDataAllowedDuringVoiceCall(boolean allow) {
+    public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.setDataAllowedDuringVoiceCall(getSubId(), allow);
+                service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -13312,27 +13358,23 @@
                 ex.rethrowAsRuntimeException();
             }
         }
-        return false;
     }
 
     /**
-     * Check whether data is allowed during voice call. This is used for allowing data on the
-     * non-default data SIM. When a voice call is placed on the non-default data SIM on DSDS
-     * devices, users will not be able to use mobile data. By calling this API, data will be
-     * temporarily enabled on the non-default data SIM during the life cycle of the voice call.
+     * Fetches the status of a piece of mobile data policy.
      *
-     * @return {@code true} if data is allowed during voice call.
-     *
-     * @throws SecurityException if the caller doesn't have the permission.
-     *
+     * @param policy The data policy that you want the status for.
+     * @return {@code true} if enabled, {@code false} otherwise.
      * @hide
      */
+    @SystemApi
+    @TestApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-    public boolean isDataAllowedInVoiceCall() {
+    public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.isDataAllowedInVoiceCall(getSubId());
+                return service.isMobileDataPolicyEnabled(getSubId(), policy);
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
@@ -13344,31 +13386,6 @@
     }
 
     /**
-     * Set whether the specific sim card always allows MMS connection. If true, MMS network
-     * request will be accepted by telephony even if user turns "mobile data" off
-     * on this specific sim card.
-     *
-     * @param alwaysAllow whether Mms data is always allowed.
-     * @return whether operation is successful.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
-        try {
-            ITelephony service = getITelephony();
-            if (service != null) {
-                return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
-            }
-        } catch (RemoteException ex) {
-            if (!isSystemProcess()) {
-                ex.rethrowAsRuntimeException();
-            }
-        }
-        return false;
-    }
-
-    /**
      * The IccLock state or password was changed successfully.
      * @hide
      */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 53069a1..0d8351d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2164,21 +2164,9 @@
      */
     String getMmsUAProfUrl(int subId);
 
-    /**
-     * Set allowing mobile data during voice call.
-     */
-    boolean setDataAllowedDuringVoiceCall(int subId, boolean allow);
+    void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
 
-    /**
-     * Check whether data is allowed during voice call. Note this is for dual sim device that
-     * data might be disabled on non-default data subscription but explicitly turned on by settings.
-     */
-    boolean isDataAllowedInVoiceCall(int subId);
-
-    /**
-     * Set whether a subscription always allows MMS connection.
-     */
-    boolean setAlwaysAllowMmsData(int subId, boolean allow);
+    boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);
 
     /**
      * Command line command to enable or disable handling of CEP data for test purposes.
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index d430db5..3b9bec9 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -54,4 +54,18 @@
         "launcher-aosp-tapl",
         "platform-test-annotations",
     ],
+}
+
+java_library {
+    name: "wm-flicker-common-assertions",
+    platform_apis: true,
+    srcs: ["src/**/*Assertions.java", "src/**/*Assertions.kt"],
+    exclude_srcs: [
+        "**/helpers/*",
+    ],
+    static_libs: [
+        "flickerlib",
+        "truth-prebuilt",
+        "app-helpers-core"
+    ],
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 69b1187..8457039 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -21,13 +21,17 @@
 import com.android.server.wm.flicker.dsl.WmAssertion
 import com.android.server.wm.flicker.helpers.WindowUtils
 
+const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
+const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+
 @JvmOverloads
 fun WmAssertion.statusBarWindowIsAlwaysVisible(
     bugId: Int = 0,
     enabled: Boolean = bugId == 0
 ) {
     all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
-        this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+        this.showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
     }
 }
 
@@ -37,7 +41,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("navBarWindowIsAlwaysVisible", enabled, bugId) {
-        this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+        this.showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
     }
 }
 
@@ -77,7 +81,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("navBarLayerIsAlwaysVisible", enabled, bugId) {
-        this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+        this.showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
     }
 }
 
@@ -87,7 +91,7 @@
     enabled: Boolean = bugId == 0
 ) {
     all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
-        this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+        this.showsLayer(STATUS_BAR_WINDOW_TITLE)
     }
 }
 
@@ -102,15 +106,15 @@
     val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
 
     start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
     }
     end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+        this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
     }
 
     if (startingPos == endingPos) {
         all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
-            this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+            this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
         }
     }
 }
@@ -126,10 +130,10 @@
     val endingPos = WindowUtils.getStatusBarPosition(endRotation)
 
     start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
     }
     end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
-        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
+        this.hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos)
     }
 }
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
deleted file mode 100644
index abe7dbc..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.kt
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker
-
-import android.os.RemoteException
-import android.os.SystemClock
-import android.platform.helpers.IAppHelper
-import android.view.Surface
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.uiautomator.UiDevice
-
-/**
- * Base class of all Flicker test that performs common functions for all flicker tests:
- *
- *
- * - Caches transitions so that a transition is run once and the transition results are used by
- * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
- * multiple times.
- * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
- * - Fails tests if results are not available for any test due to jank.
- */
-abstract class FlickerTestBase {
-    val instrumentation by lazy {
-        InstrumentationRegistry.getInstrumentation()
-    }
-    val uiDevice by lazy {
-        UiDevice.getInstance(instrumentation)
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param rotation Initial screen rotation
-     *
-     * @return test tag with pattern <NAME>__<APP>__<ROTATION>
-    </ROTATION></APP></NAME> */
-    protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
-        return buildTestTag(
-                testName, app, rotation, rotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     *
-     * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
-    </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
-    protected fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int
-    ): String {
-        return buildTestTag(
-                testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
-    }
-
-    /**
-     * Build a test tag for the test
-     * @param testName Name of the transition(s) being tested
-     * @param app App being launcher
-     * @param app2 Second app being launched (if any)
-     * @param beginRotation Initial screen rotation
-     * @param endRotation End screen rotation (if any, otherwise use same as initial)
-     * @param extraInfo Additional information to append to the tag
-     *
-     * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
-    </EXTRA></NAME> */
-    protected fun buildTestTag(
-        testName: String,
-        app: IAppHelper,
-        beginRotation: Int,
-        endRotation: Int,
-        app2: IAppHelper?,
-        extraInfo: String
-    ): String {
-        var testTag = "${testName}__${app.launcherName}"
-        if (app2 != null) {
-            testTag += "-${app2.launcherName}"
-        }
-        testTag += "__${Surface.rotationToString(beginRotation)}"
-        if (endRotation != beginRotation) {
-            testTag += "-${Surface.rotationToString(endRotation)}"
-        }
-        if (extraInfo.isNotEmpty()) {
-            testTag += "__$extraInfo"
-        }
-        return testTag
-    }
-
-    protected fun Flicker.setRotation(rotation: Int) {
-        try {
-            when (rotation) {
-                Surface.ROTATION_270 -> device.setOrientationLeft()
-                Surface.ROTATION_90 -> device.setOrientationRight()
-                Surface.ROTATION_0 -> device.setOrientationNatural()
-                else -> device.setOrientationNatural()
-            }
-            // Wait for animation to complete
-            SystemClock.sleep(1000)
-        } catch (e: RemoteException) {
-            throw RuntimeException(e)
-        }
-    }
-
-    companion object {
-        const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
-        const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
-        const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
deleted file mode 100644
index e7d1f8e..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/NonRotationTestBase.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class NonRotationTestBase(
-    protected val rotationName: String,
-    protected val rotation: Int
-) : FlickerTestBase() {
-    companion object {
-        const val SCREENSHOT_LAYER = "RotationLayer"
-
-        @Parameterized.Parameters(name = "{0}")
-        @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
deleted file mode 100644
index 3b67727..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/RotationTestBase.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker
-
-import android.view.Surface
-import org.junit.runners.Parameterized
-
-abstract class RotationTestBase(
-    beginRotationName: String,
-    endRotationName: String,
-    protected val beginRotation: Int,
-    protected val endRotation: Int
-) : FlickerTestBase() {
-    companion object {
-        @Parameterized.Parameters(name = "{0}-{1}")
-        @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params: MutableCollection<Array<Any>> = mutableListOf()
-            for (begin in supportedRotations) {
-                for (end in supportedRotations) {
-                    if (begin != end) {
-                        params.add(arrayOf(
-                                Surface.rotationToString(begin),
-                                Surface.rotationToString(end),
-                                begin,
-                                end
-                        ))
-                    }
-                }
-            }
-            return params
-        }
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
new file mode 100644
index 0000000..742003a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -0,0 +1,110 @@
+/*
+ * 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.server.wm.flicker.helpers
+
+import android.os.Bundle
+import android.os.RemoteException
+import android.os.SystemClock
+import android.platform.helpers.IAppHelper
+import android.view.Surface
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.startRotation
+
+fun Flicker.setRotation(rotation: Int) {
+    try {
+        when (rotation) {
+            Surface.ROTATION_270 -> device.setOrientationLeft()
+            Surface.ROTATION_90 -> device.setOrientationRight()
+            Surface.ROTATION_0 -> device.setOrientationNatural()
+            else -> device.setOrientationNatural()
+        }
+        // Wait for animation to complete
+        SystemClock.sleep(1000)
+    } catch (e: RemoteException) {
+        throw RuntimeException(e)
+    }
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: IAppHelper,
+    beginRotation: Int,
+    endRotation: Int
+): String {
+    return buildTestTag(
+        testName, app.launcherName, beginRotation, endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param rotation Screen rotation configuration for the test
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: IAppHelper?,
+    configuration: Bundle
+): String {
+    return buildTestTag(testName, app?.launcherName ?: "", configuration.startRotation,
+        configuration.endRotation, app2 = null, extraInfo = "")
+}
+
+/**
+ * Build a test tag for the test
+ * @param testName Name of the transition(s) being tested
+ * @param app App being launcher
+ * @param app2 Second app being launched (if any)
+ * @param beginRotation Initial screen rotation
+ * @param endRotation End screen rotation (if any, otherwise use same as initial)
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+</EXTRA></NAME> */
+fun buildTestTag(
+    testName: String,
+    app: String,
+    beginRotation: Int,
+    endRotation: Int,
+    app2: String?,
+    extraInfo: String
+): String {
+    var testTag = "${testName}__$app"
+    if (app2 != null) {
+        testTag += "-$app2"
+    }
+    testTag += "__${Surface.rotationToString(beginRotation)}"
+    if (endRotation != beginRotation) {
+        testTag += "-${Surface.rotationToString(endRotation)}"
+    }
+    if (extraInfo.isNotEmpty()) {
+        testTag += "__$extraInfo"
+    }
+    return testTag
+}
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 404c789..a73264d 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
@@ -16,21 +16,27 @@
 
 package com.android.server.wm.flicker.ime
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,56 +45,62 @@
  * Test IME window closing back to app window transitions.
  * To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToAppTest(
-    rotationName: String,
-    rotation: Int
-) : CloseImeWindowToAppTest(rotationName, rotation) {
-    override val testApp: ImeAppHelper
-        get() = ImeAppAutoFocusHelper(instrumentation)
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    override fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToAppAutoOpen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressBack()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppAutoFocusHelper(instrumentation)
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-                    imeLayerBecomesInvisible(bugId = 141458352)
-                    imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("imeToAppAutoOpen", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressBack()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeAppWindowIsAlwaysVisible(testApp, bugId = 141458352)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+                            imeLayerBecomesInvisible(bugId = 141458352)
+                            imeAppLayerIsAlwaysVisible(testApp, bugId = 141458352)
+                        }
+                    }
                 }
-            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index c1ba21a..7647802 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
-import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,53 +50,63 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class CloseImeAutoOpenWindowToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : CloseImeWindowToHomeTest(rotationName, rotation) {
-    override val testApp: ImeAppHelper
-        get() = ImeAppAutoFocusHelper(instrumentation)
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    override fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToHomeAutoOpen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressHome()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeWindowBecomesInvisible(bugId = 141458352)
-                    imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppAutoFocusHelper(instrumentation)
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-                    imeLayerBecomesInvisible(bugId = 141458352)
-                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("imeToHomeAutoOpen", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressHome()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeWindowBecomesInvisible(bugId = 141458352)
+                            imeAppWindowBecomesInvisible(testApp, bugId = 157449248)
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                            imeLayerBecomesInvisible(bugId = 141458352)
+                            imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        }
+                    }
                 }
-            }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2c00722..136cf86 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -44,52 +49,57 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToAppTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToAppTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    open fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                    testApp.openIME(device)
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                device.pressBack()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeAppWindowIsAlwaysVisible(testApp)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("imeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                            testApp.openIME(device)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    transitions {
+                        device.pressBack()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeAppWindowIsAlwaysVisible(testApp)
+                        }
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-                    imeLayerBecomesInvisible(enabled = false)
-                    imeAppLayerIsAlwaysVisible(testApp)
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+                            imeLayerBecomesInvisible(enabled = false)
+                            imeAppLayerIsAlwaysVisible(testApp)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 4697adc..6cfb282 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -19,21 +19,26 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.openQuickstep
 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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,61 +50,69 @@
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeWindowToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    open val testApp = ImeAppHelper(instrumentation)
+class CloseImeWindowToHomeTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
 
-    @Test
-    open fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("imeToHome", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                }
-                eachRun {
-                    device.openQuickstep()
-                    device.reopenAppFromOverview()
-                    this.setRotation(rotation)
-                    testApp.openIME(device)
-                }
-            }
-            transitions {
-                device.pressHome()
-                device.waitForIdle()
-            }
-            teardown {
-                eachRun {
-                    device.pressHome()
-                }
-                test {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    imeWindowBecomesInvisible()
-                    imeAppWindowBecomesInvisible(testApp)
-                }
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("imeToHome", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                        }
+                        eachRun {
+                            device.openQuickstep()
+                            device.reopenAppFromOverview()
+                            this.setRotation(configuration.startRotation)
+                            testApp.openIME(device)
+                        }
+                    }
+                    transitions {
+                        device.pressHome()
+                        device.waitForIdle()
+                    }
+                    teardown {
+                        eachRun {
+                            device.pressHome()
+                        }
+                        test {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            imeWindowBecomesInvisible()
+                            imeAppWindowBecomesInvisible(testApp)
+                        }
 
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-                    imeLayerBecomesInvisible(bugId = 153739621)
-                    imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                Surface.ROTATION_0, allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                            imeLayerBecomesInvisible(bugId = 153739621)
+                            imeAppLayerBecomesInvisible(testApp, bugId = 153739621)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
similarity index 100%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/ime/ImeAssertions.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 2caa8f3..5767a94 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -19,19 +19,24 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -45,62 +50,65 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenImeWindowTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = ImeAppHelper(instrumentation)
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("openIme", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    testApp.open()
-                }
-            }
-            transitions {
-                testApp.openIME(device)
-            }
-            teardown {
-                eachRun {
-                    testApp.closeIME(device)
-                }
-                test {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("imeWindowBecomesVisible") {
-                        this.skipUntilFirstAssertion()
-                            .hidesNonAppWindow(IME_WINDOW_TITLE)
-                            .then()
-                            .showsNonAppWindow(IME_WINDOW_TITLE)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    imeLayerBecomesVisible()
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val IME_WINDOW_TITLE = "InputMethod"
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = ImeAppHelper(instrumentation)
+
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("openIme", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            testApp.open()
+                        }
+                    }
+                    transitions {
+                        testApp.openIME(device)
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.closeIME(device)
+                        }
+                        test {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("imeWindowBecomesVisible") {
+                                this.skipUntilFirstAssertion()
+                                    .hidesNonAppWindow(IME_WINDOW_TITLE)
+                                    .then()
+                                    .showsNonAppWindow(IME_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation)
+                            navBarLayerRotatesAndScales(configuration.startRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation)
+
+                            imeLayerBecomesVisible()
+                        }
+                    }
+                }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
new file mode 100644
index 0000000..7e857f3
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.server.wm.flicker.launch
+
+import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+
+fun WmAssertion.wallpaperWindowBecomesInvisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperWindowBecomesInvisible", enabled, bugId) {
+        this.showsBelowAppWindow("Wallpaper")
+                .then()
+                .hidesBelowAppWindow("Wallpaper")
+    }
+}
+
+fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
+        this.showsAppWindowOnTop("Launcher")
+                .then()
+                .showsAppWindowOnTop("Snapshot", testApp.getPackage())
+    }
+}
+
+fun LayersAssertion.wallpaperLayerBecomesInvisible(
+    testApp: IAppHelper,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("wallpaperLayerBecomesInvisible", enabled, bugId) {
+        this.showsLayer("Wallpaper")
+                .then()
+                .replaceVisibleLayer("Wallpaper", testApp.getPackage())
+    }
+}
\ No newline at end of file
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 2c9c8ba..1081414 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
@@ -19,18 +19,26 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -44,53 +52,64 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppColdTest(
-    rotationName: String,
-    rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("openAppCold", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    this.setRotation(rotation)
-                }
-            }
-            transitions {
-                testApp.open()
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    appWindowReplacesLauncherAsTopWindow()
-                    wallpaperWindowBecomesInvisible()
-                }
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("openAppCold", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    transitions {
+                        testApp.open()
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            appWindowReplacesLauncherAsTopWindow(testApp)
+                            wallpaperWindowBecomesInvisible()
+                        }
 
-                layersTrace {
-                    // During testing the launcher is always in portrait mode
-                    noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
-                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
-                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
-                    statusBarLayerIsAlwaysVisible(enabled = false)
-                    wallpaperLayerBecomesInvisible()
-                }
+                        layersTrace {
+                            // During testing the launcher is always in portrait mode
+                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                bugId = 141361128)
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            navBarLayerIsAlwaysVisible(
+                                enabled = configuration.endRotation == Surface.ROTATION_0)
+                            statusBarLayerIsAlwaysVisible(enabled = false)
+                            wallpaperLayerBecomesInvisible(testApp)
+                        }
 
-                eventLog {
-                    focusChanges("NexusLauncherActivity", testApp.`package`)
+                        eventLog {
+                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        }
+                    }
                 }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
deleted file mode 100644
index 98e05d5..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTestBase.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.launch
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertion
-import com.android.server.wm.flicker.dsl.WmAssertion
-
-abstract class OpenAppTestBase(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    protected val testApp = StandardAppHelper(instrumentation,
-            "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-    protected fun WmAssertion.wallpaperWindowBecomesInvisible(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("wallpaperWindowBecomesInvisible", enabled, bugId) {
-            this.showsBelowAppWindow("Wallpaper")
-                    .then()
-                    .hidesBelowAppWindow("Wallpaper")
-        }
-    }
-
-    protected fun WmAssertion.appWindowReplacesLauncherAsTopWindow(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
-            this.showsAppWindowOnTop("Launcher")
-                    .then()
-                    .showsAppWindowOnTop("Snapshot", testApp.getPackage())
-        }
-    }
-
-    protected fun LayersAssertion.wallpaperLayerBecomesInvisible(
-        bugId: Int = 0,
-        enabled: Boolean = bugId == 0
-    ) {
-        all("appWindowReplacesLauncherAsTopWindow", enabled, bugId) {
-            this.showsLayer("Wallpaper")
-                    .then()
-                    .replaceVisibleLayer("Wallpaper", testApp.getPackage())
-        }
-    }
-}
\ 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 acd141a..2061994 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,21 +16,29 @@
 
 package com.android.server.wm.flicker.launch
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -39,64 +47,73 @@
  * Test warm launch app.
  * To run this test: `atest FlickerTests:OpenAppWarmTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class OpenAppWarmTest(
-    rotationName: String,
-    rotation: Int
-) : OpenAppTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTag { buildTestTag("openAppWarm", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.open()
+                        }
+                        eachRun {
+                            device.pressHome()
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    transitions {
+                        testApp.open()
+                    }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                            appWindowReplacesLauncherAsTopWindow(testApp)
+                            wallpaperWindowBecomesInvisible(enabled = false)
+                        }
 
-        flicker(instrumentation) {
-            withTag { buildTestTag("openAppWarm", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    testApp.open()
-                }
-                eachRun {
-                    device.pressHome()
-                    this.setRotation(rotation)
-                }
-            }
-            transitions {
-                testApp.open()
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    appWindowReplacesLauncherAsTopWindow()
-                    wallpaperWindowBecomesInvisible(enabled = false)
-                }
+                        layersTrace {
+                            // During testing the launcher is always in portrait mode
+                            noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+                                bugId = 141361128)
+                            navBarLayerRotatesAndScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(Surface.ROTATION_0,
+                                configuration.endRotation)
+                            navBarLayerIsAlwaysVisible(
+                                enabled = configuration.endRotation == Surface.ROTATION_0)
+                            statusBarLayerIsAlwaysVisible(enabled = false)
+                            wallpaperLayerBecomesInvisible(testApp)
+                        }
 
-                layersTrace {
-                    // During testing the launcher is always in portrait mode
-                    noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128)
-                    navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation)
-                    statusBarLayerRotatesScales(Surface.ROTATION_0, rotation)
-                    navBarLayerIsAlwaysVisible(enabled = rotation == Surface.ROTATION_0)
-                    statusBarLayerIsAlwaysVisible(enabled = false)
-                    wallpaperLayerBecomesInvisible()
+                        eventLog {
+                            focusChanges("NexusLauncherActivity", testApp.`package`)
+                        }
+                    }
                 }
-
-                eventLog {
-                    focusChanges("NexusLauncherActivity", testApp.`package`)
-                }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
similarity index 78%
copy from media/java/android/media/tv/TvChannelInfo.aidl
copy to tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
index 71cd0a7..6bc9dcb 100644
--- a/media/java/android/media/tv/TvChannelInfo.aidl
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/CommonAssertions.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.media.tv;
+package com.android.server.wm.flicker.pip
 
-parcelable TvChannelInfo;
+internal const val PIP_WINDOW_TITLE = "PipMenuActivity"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
index 9cfc033..89539fd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/EnterPipTest.kt
@@ -16,23 +16,31 @@
 
 package com.android.server.wm.flicker.pip
 
+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.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -41,81 +49,85 @@
  * Test Pip launch.
  * To run this test: `atest FlickerTests:PipToAppTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class EnterPipTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("enterPip", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    device.pressHome()
-                    testApp.open()
-                    this.setRotation(rotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                    testApp.exit()
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                }
-            }
-            transitions {
-                testApp.clickEnterPipButton(device)
-                device.expandPipWindow()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                    all("pipWindowBecomesVisible") {
-                        this.showsAppWindow(testApp.`package`)
-                                .then()
-                                .showsAppWindow(sPipWindowTitle)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
-                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
-
-                    all("pipLayerBecomesVisible") {
-                        this.showsLayer(testApp.launcherName)
-                                .then()
-                                .showsLayer(sPipWindowTitle)
-                    }
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("enterPip", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            device.pressHome()
+                            testApp.open()
+                            this.setRotation(configuration.startRotation)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                        }
+                    }
+                    transitions {
+                        testApp.clickEnterPipButton(device)
+                        device.expandPipWindow()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("pipWindowBecomesVisible") {
+                                this.showsAppWindow(testApp.`package`)
+                                    .then()
+                                    .showsAppWindow(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+                        }
+
+                        layersTrace {
+                            all("pipLayerBecomesVisible") {
+                                this.showsLayer(testApp.launcherName)
+                                    .then()
+                                    .showsLayer(PIP_WINDOW_TITLE)
+                            }
+                        }
+                    }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
deleted file mode 100644
index 691db7fb..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipTestBase.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.pip
-
-import com.android.server.wm.flicker.NonRotationTestBase
-import com.android.server.wm.flicker.helpers.PipAppHelper
-
-abstract class PipTestBase(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    protected val testApp = PipAppHelper(instrumentation)
-
-    companion object {
-        const val sPipWindowTitle = "PipMenuActivity"
-    }
-}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
index deccc90..ac54a0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToAppTest.kt
@@ -19,21 +19,28 @@
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.expandPipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -47,73 +54,82 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToAppTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    device.pressHome()
-                    testApp.open()
-                }
-                eachRun {
-                    this.setRotation(rotation)
-                    testApp.clickEnterPipButton(device)
-                    device.hasPipWindow()
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            device.pressHome()
+                            testApp.open()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                            testApp.clickEnterPipButton(device)
+                            device.hasPipWindow()
+                        }
                     }
-                    testApp.exit()
-                }
-            }
-            transitions {
-                device.expandPipWindow()
-                device.waitForIdle()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        device.expandPipWindow()
+                        device.waitForIdle()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
 
-                    all("appReplacesPipWindow") {
-                        this.showsAppWindow(sPipWindowTitle)
-                                .then()
-                                .showsAppWindowOnTop(testApp.launcherName)
+                            all("appReplacesPipWindow") {
+                                this.showsAppWindow(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .showsAppWindowOnTop(testApp.launcherName)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+
+                            all("appReplacesPipLayer") {
+                                this.showsLayer(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .showsLayer(testApp.launcherName)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(
+                                "NexusLauncherActivity", testApp.launcherName,
+                                "NexusLauncherActivity", bugId = 151179149)
+                        }
                     }
                 }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("appReplacesPipLayer") {
-                        this.showsLayer(sPipWindowTitle)
-                                .then()
-                                .showsLayer(testApp.launcherName)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(
-                            "NexusLauncherActivity", testApp.launcherName, "NexusLauncherActivity",
-                            bugId = 151179149)
-                }
-            }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
index f40869c..f14a27d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/pip/PipToHomeTest.kt
@@ -19,20 +19,27 @@
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.closePipWindow
 import com.android.server.wm.flicker.helpers.hasPipWindow
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -46,83 +53,83 @@
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 152738416)
 class PipToHomeTest(
-    rotationName: String,
-    rotation: Int
-) : PipTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        flicker(instrumentation) {
-            withTag { buildTestTag("exitPipModeToApp", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    device.pressHome()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                    testApp.clickEnterPipButton(device)
-                    device.hasPipWindow()
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                }
-                test {
-                    if (device.hasPipWindow()) {
-                        device.closePipWindow()
-                    }
-                    testApp.exit()
-                }
-            }
-            transitions {
-                testApp.closePipWindow(device)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("pipWindowBecomesInvisible") {
-                        this.showsAppWindow(sPipWindowTitle)
-                                .then()
-                                .hidesAppWindow(sPipWindowTitle)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    // The final state is the launcher, so always in portrait mode
-                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("pipLayerBecomesInvisible") {
-                        this.showsLayer(sPipWindowTitle)
-                                .then()
-                                .hidesLayer(sPipWindowTitle)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(testApp.launcherName, "NexusLauncherActivity", bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
-        fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        fun getParams(): List<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = PipAppHelper(instrumentation)
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName { buildTestTag("exitPipModeToApp", testApp, configuration) }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            device.pressHome()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.startRotation)
+                            testApp.clickEnterPipButton(device)
+                            device.hasPipWindow()
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                        }
+                        test {
+                            if (device.hasPipWindow()) {
+                                device.closePipWindow()
+                            }
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        testApp.closePipWindow(device)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("pipWindowBecomesInvisible") {
+                                this.showsAppWindow(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .hidesAppWindow(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0,
+                                enabled = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                Surface.ROTATION_0, bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                Surface.ROTATION_0)
+
+                            all("pipLayerBecomesInvisible") {
+                                this.showsLayer(PIP_WINDOW_TITLE)
+                                    .then()
+                                    .hidesLayer(PIP_WINDOW_TITLE)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(testApp.launcherName, "NexusLauncherActivity",
+                                bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 99218c2..24ca311 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,24 +16,30 @@
 
 package com.android.server.wm.flicker.rotation
 
+import android.platform.test.annotations.Presubmit
 import androidx.test.filters.RequiresDevice
 import android.view.Surface
-import com.android.server.wm.flicker.NonRotationTestBase.Companion.SCREENSHOT_LAYER
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.StandardAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.buildTestTag
 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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -42,84 +48,95 @@
  * Cycle through supported app rotations.
  * To run this test: `atest FlickerTest:ChangeAppRotationTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class ChangeAppRotationTest(
-    beginRotationName: String,
-    endRotationName: String,
-    beginRotation: Int,
-    endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
+    companion object {
+        private const val SCREENSHOT_LAYER = "RotationLayer"
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
                 "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag {
-                buildTestTag("changeAppRotation", testApp, beginRotation, endRotation)
-            }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                    testApp.open()
-                }
-                eachRun {
-                    this.setRotation(beginRotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            transitions {
-                this.setRotation(endRotation)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(beginRotation, endRotation, allStates = false)
-                    navBarLayerRotatesAndScales(beginRotation, endRotation)
-                    statusBarLayerRotatesScales(beginRotation, endRotation)
-                }
-
-                layersTrace {
-                    val startingPos = WindowUtils.getDisplayBounds(beginRotation)
-                    val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
-                    start("appLayerRotates_StartingPos") {
-                        this.hasVisibleRegion(testApp.getPackage(), startingPos)
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildRotationTest { configuration ->
+                    withTestName {
+                        buildTestTag(
+                            "changeAppRotation", testApp, configuration)
                     }
-
-                    end("appLayerRotates_EndingPos") {
-                        this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                            testApp.open()
+                        }
+                        eachRun {
+                            this.setRotation(configuration.startRotation)
+                        }
                     }
+                    teardown {
+                        eachRun {
+                            this.setRotation(Surface.ROTATION_0)
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        this.setRotation(configuration.endRotation)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
 
-                    all("screenshotLayerBecomesInvisible") {
-                        this.showsLayer(testApp.getPackage())
-                                .then()
-                                .showsLayer(SCREENSHOT_LAYER)
-                                .then()
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                            noUncoveredRegions(configuration.startRotation,
+                                configuration.endRotation, allStates = false)
+                            navBarLayerRotatesAndScales(configuration.startRotation,
+                                configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.startRotation,
+                                configuration.endRotation)
+                        }
+
+                        layersTrace {
+                            val startingPos = WindowUtils.getDisplayBounds(
+                                configuration.startRotation)
+                            val endingPos = WindowUtils.getDisplayBounds(
+                                configuration.endRotation)
+
+                            start("appLayerRotates_StartingPos") {
+                                this.hasVisibleRegion(testApp.getPackage(), startingPos)
+                            }
+
+                            end("appLayerRotates_EndingPos") {
+                                this.hasVisibleRegion(testApp.getPackage(), endingPos)
+                            }
+
+                            all("screenshotLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                        .then()
+                                        .showsLayer(SCREENSHOT_LAYER)
+                                        .then()
                                 showsLayer(testApp.getPackage())
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
                     }
                 }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 33a823d..8ad6c46 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -16,29 +16,36 @@
 
 package com.android.server.wm.flicker.rotation
 
+import android.content.ComponentName
 import android.content.Intent
-import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.os.Bundle
+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 androidx.test.uiautomator.By
 import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.RotationTestBase
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.stopPackage
 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.testapp.ActivityOptions
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -47,150 +54,142 @@
  * Cycle through supported app rotations using seamless rotations.
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 147659548)
 class SeamlessAppRotationTest(
-    testId: String,
-    private val intent: Intent,
-    beginRotationName: String,
-    endRotationName: String,
-    beginRotation: Int,
-    endRotation: Int
-) : RotationTestBase(beginRotationName, endRotationName, beginRotation, endRotation) {
-    @Test
-    fun test() {
-        var intentId = ""
-        if (intent.extras?.getBoolean(ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
-            intentId = "BUSY_UI_THREAD"
-        }
-
-        flicker(instrumentation) {
-            withTag {
-                "changeAppRotation_" + intentId + "_" +
-                        Surface.rotationToString(beginRotation) + "_" +
-                        Surface.rotationToString(endRotation)
-            }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    instrumentation.targetContext.startActivity(intent)
-                    device.wait(Until.hasObject(By.pkg(intent.component?.packageName)
-                            .depth(0)), APP_LAUNCH_TIMEOUT)
-                    this.setRotation(beginRotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    stopPackage(
-                            instrumentation.targetContext,
-                            intent.component?.packageName
-                                    ?: error("Unable to determine package name for intent"))
-                    this.setRotation(Surface.ROTATION_0)
-                }
-            }
-            transitions {
-                this.setRotation(endRotation)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible(bugId = 140855415)
-                    statusBarWindowIsAlwaysVisible(bugId = 140855415)
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible(bugId = 140855415)
-                    noUncoveredRegions(beginRotation, endRotation, allStates = true)
-                    navBarLayerRotatesAndScales(beginRotation, endRotation)
-                    statusBarLayerRotatesScales(beginRotation, endRotation, enabled = false)
-                }
-
-                layersTrace {
-                    all("appLayerRotates"/*, bugId = 147659548*/) {
-                        val startingPos = WindowUtils.getDisplayBounds(beginRotation)
-                        val endingPos = WindowUtils.getDisplayBounds(endRotation)
-
-                        if (startingPos == endingPos) {
-                            this.hasVisibleRegion(
-                                    intent.component?.packageName ?: "",
-                                    startingPos)
-                        } else {
-                            this.hasVisibleRegion(intent.component?.packageName ?: "", startingPos)
-                                    .then()
-                                    .hasVisibleRegion(intent.component?.packageName
-                                            ?: "", endingPos)
-                        }
-                    }
-
-                    all("noUncoveredRegions"/*, bugId = 147659548*/) {
-                        val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
-                        val endingBounds = WindowUtils.getDisplayBounds(endRotation)
-                        if (startingBounds == endingBounds) {
-                            this.coversAtLeastRegion(startingBounds)
-                        } else {
-                            this.coversAtLeastRegion(startingBounds)
-                                    .then()
-                                    .coversAtLeastRegion(endingBounds)
-                        }
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val APP_LAUNCH_TIMEOUT: Long = 10000
 
-        // launch test activity that supports seamless rotation with a busy UI thread to miss frames
-        // when the app is asked to redraw
+        private val Bundle.intent: Intent?
+            get() = this.getParcelable(Intent::class.java.simpleName)
+
+        private val Bundle.intentPackageName: String
+            get() = this.intent?.component?.packageName ?: ""
+
+        private val Bundle.intentId get() = if (this.intent?.getBooleanExtra(
+                ActivityOptions.EXTRA_STARVE_UI_THREAD, false) == true) {
+            "BUSY_UI_THREAD"
+        } else {
+            ""
+        }
+
+        private fun Bundle.createConfig(starveUiThread: Boolean): Bundle {
+            val config = this.deepCopy()
+            val intent = Intent()
+            intent.addCategory(Intent.CATEGORY_LAUNCHER)
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            intent.component = ComponentName("com.android.server.wm.flicker.testapp",
+                "com.android.server.wm.flicker.testapp.SeamlessRotationActivity")
+
+            intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, starveUiThread)
+
+            config.putParcelable(Intent::class.java.simpleName, intent)
+            return config
+        }
+
+        @JvmStatic
+        private fun FlickerTestRunnerFactory.getConfigurations(): List<Bundle> {
+            return this.getConfigRotationTests().flatMap {
+                val defaultRun = it.createConfig(starveUiThread = false)
+                val busyUiRun = it.createConfig(starveUiThread = true)
+                listOf(defaultRun, busyUiRun)
+            }
+        }
+
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
-            val params = mutableListOf<Array<Any>>()
-            val testIntents = mutableListOf<Intent>()
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val factory = FlickerTestRunnerFactory(instrumentation)
+            val configurations = factory.getConfigurations()
+            return factory.buildRotationTest(configurations) { configuration ->
+                withTestName {
+                    buildTestTag("seamlessRotation_" + configuration.intentId,
+                        app = null, configuration = configuration)
+                }
+                repeat { configuration.repetitions }
+                setup {
+                    test {
+                        device.wakeUpAndGoToHomeScreen()
+                        instrumentation.targetContext.startActivity(configuration.intent)
+                        val searchQuery = By.pkg(configuration.intent?.component?.packageName)
+                            .depth(0)
+                        device.wait(Until.hasObject(searchQuery), APP_LAUNCH_TIMEOUT)
+                    }
+                    eachRun {
+                        this.setRotation(configuration.startRotation)
+                    }
+                }
+                teardown {
+                    test {
+                        this.setRotation(Surface.ROTATION_0)
+                        stopPackage(
+                            instrumentation.targetContext,
+                            configuration.intent?.component?.packageName
+                                ?: error("Unable to determine package name for intent"))
+                    }
+                }
+                transitions {
+                    this.setRotation(configuration.endRotation)
+                }
+                assertions {
+                    windowManagerTrace {
+                        navBarWindowIsAlwaysVisible(bugId = 140855415)
+                        statusBarWindowIsAlwaysVisible(bugId = 140855415)
+                    }
 
-            // launch test activity that supports seamless rotation
-            var intent = Intent(Intent.ACTION_MAIN)
-            intent.component = ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME
-            intent.flags = FLAG_ACTIVITY_NEW_TASK
-            testIntents.add(intent)
+                    layersTrace {
+                        navBarLayerIsAlwaysVisible(bugId = 140855415)
+                        statusBarLayerIsAlwaysVisible(bugId = 140855415)
+                        noUncoveredRegions(configuration.startRotation,
+                            configuration.endRotation, allStates = false
+                            /*, bugId = 147659548*/)
+                        navBarLayerRotatesAndScales(configuration.startRotation,
+                            configuration.endRotation)
+                        statusBarLayerRotatesScales(configuration.startRotation,
+                            configuration.endRotation, enabled = false)
+                    }
 
-            // launch test activity that supports seamless rotation with a busy UI thread to miss frames
-            // when the app is asked to redraw
-            intent = Intent(intent)
-            intent.putExtra(ActivityOptions.EXTRA_STARVE_UI_THREAD, true)
-            intent.flags = FLAG_ACTIVITY_NEW_TASK
-            testIntents.add(intent)
-            for (testIntent in testIntents) {
-                for (begin in supportedRotations) {
-                    for (end in supportedRotations) {
-                        if (begin != end) {
-                            var testId: String = Surface.rotationToString(begin) +
-                                    "_" + Surface.rotationToString(end)
-                            if (testIntent.extras?.getBoolean(
-                                            ActivityOptions.EXTRA_STARVE_UI_THREAD) == true) {
-                                testId += "_" + "BUSY_UI_THREAD"
+                    layersTrace {
+                        val startingBounds = WindowUtils
+                            .getDisplayBounds(configuration.startRotation)
+                        val endingBounds = WindowUtils
+                            .getDisplayBounds(configuration.endRotation)
+
+                        all("appLayerRotates"/*, bugId = 147659548*/) {
+                            if (startingBounds == endingBounds) {
+                                this.hasVisibleRegion(
+                                    configuration.intentPackageName, startingBounds)
+                            } else {
+                                this.hasVisibleRegion(configuration.intentPackageName,
+                                    startingBounds)
+                                    .then()
+                                    .hasVisibleRegion(configuration.intentPackageName,
+                                        endingBounds)
                             }
-                            params.add(arrayOf(
-                                    testId,
-                                    testIntent,
-                                    Surface.rotationToString(begin),
-                                    Surface.rotationToString(end),
-                                    begin,
-                                    end))
                         }
+
+                        all("noUncoveredRegions"/*, bugId = 147659548*/) {
+                            if (startingBounds == endingBounds) {
+                                this.coversAtLeastRegion(startingBounds)
+                            } else {
+                                this.coversAtLeastRegion(startingBounds)
+                                    .then()
+                                    .coversAtLeastRegion(endingBounds)
+                            }
+                        }
+                    }
+
+                    eventLog {
+                        focusDoesNotChange(bugId = 151179149)
                     }
                 }
             }
-            return params
         }
     }
 }
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index 3b5e669..ae9fcf9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -16,25 +16,32 @@
 
 package com.android.server.wm.flicker.splitscreen
 
-import android.view.Surface
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
+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.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
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -43,78 +50,79 @@
  * Test open app to split screen.
  * To run this test: `atest FlickerTests:OpenAppToSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 161435597)
 class OpenAppToSplitScreenTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
-        "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("appToSplitScreen", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                }
-            }
-            teardown {
-                eachRun {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-                test {
-                    testApp.exit()
-                }
-            }
-            transitions {
-                device.launchSplitScreen()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible(bugId = 140855415)
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation, enabled = false)
-                    navBarLayerRotatesAndScales(rotation, bugId = 140855415)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("dividerLayerBecomesVisible") {
-                        this.hidesLayer(DOCKED_STACK_DIVIDER)
-                                .then()
-                                .showsLayer(DOCKED_STACK_DIVIDER)
-                    }
-                }
-
-                eventLog {
-                    focusChanges(testApp.`package`,
-                            "recents_animation_input_consumer", "NexusLauncherActivity",
-                            bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+
+            return FlickerTestRunnerFactory(instrumentation)
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("appToSplitScreen", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.endRotation)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                        test {
+                            testApp.exit()
+                        }
+                    }
+                    transitions {
+                        device.launchSplitScreen()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible(bugId = 140855415)
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation, enabled = false)
+                            navBarLayerRotatesAndScales(configuration.endRotation,
+                                bugId = 140855415)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            all("dividerLayerBecomesVisible") {
+                                this.hidesLayer(DOCKED_STACK_DIVIDER)
+                                    .then()
+                                    .showsLayer(DOCKED_STACK_DIVIDER)
+                            }
+                        }
+
+                        eventLog {
+                            focusChanges(testApp.`package`,
+                                "recents_animation_input_consumer", "NexusLauncherActivity",
+                                bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
index abf41a1..4b9f024 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/ResizeSplitScreenTest.kt
@@ -19,32 +19,39 @@
 import android.graphics.Region
 import android.util.Rational
 import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.uiautomator.By
-import com.android.server.wm.flicker.FlickerTestBase
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
 import com.android.server.wm.flicker.focusDoesNotChange
 import com.android.server.wm.flicker.helpers.ImeAppHelper
 import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.resizeSplitScreen
+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 org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
 
 /**
  * Test split screen resizing window transitions.
@@ -53,138 +60,149 @@
  * Currently it runs only in 0 degrees because of b/156100803
  */
 @RequiresDevice
-@RunWith(AndroidJUnit4::class)
+@RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @FlakyTest(bugId = 159096424)
-class ResizeSplitScreenTest : FlickerTestBase() {
-    @Test
-    fun test() {
-        val testAppTop = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-        val testAppBottom = ImeAppHelper(instrumentation)
-
-        flicker(instrumentation) {
-            withTag {
-                val description = (startRatio.toString().replace("/", "-") + "_to_" +
-                        stopRatio.toString().replace("/", "-"))
-                buildTestTag("resizeSplitScreen", testAppTop, rotation,
-                        rotation, testAppBottom, description)
-            }
-            repeat { 1 }
-            setup {
-                eachRun {
-                    device.wakeUpAndGoToHomeScreen()
-                    this.setRotation(rotation)
-                    this.launcherStrategy.clearRecentAppsFromOverview()
-                    testAppBottom.open()
-                    device.pressHome()
-                    testAppTop.open()
-                    device.waitForIdle()
-                    device.launchSplitScreen()
-                    val snapshot = device.findObject(By.res(device.launcherPackageName, "snapshot"))
-                    snapshot.click()
-                    testAppBottom.openIME(device)
-                    device.pressBack()
-                    device.resizeSplitScreen(startRatio)
-                }
-            }
-            teardown {
-                eachRun {
-                    device.exitSplitScreen()
-                    device.pressHome()
-                    testAppTop.exit()
-                    testAppBottom.exit()
-                }
-                test {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-            }
-            transitions {
-                device.resizeSplitScreen(stopRatio)
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-
-                    all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
-                        this.showsAppWindow(sSimpleActivity)
-                    }
-
-                    all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
-                        this.showsAppWindow(sImeActivity)
-                    }
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    all("topAppLayerIsAlwaysVisible") {
-                        this.showsLayer(sSimpleActivity)
-                    }
-
-                    all("bottomAppLayerIsAlwaysVisible") {
-                        this.showsLayer(sImeActivity)
-                    }
-
-                    all("dividerLayerIsAlwaysVisible") {
-                        this.showsLayer(DOCKED_STACK_DIVIDER)
-                    }
-
-                    start("appsStartingBounds", enabled = false) {
-                        val displayBounds = WindowUtils.displayBounds
-                        val entry = this.trace.entries.firstOrNull()
-                                ?: throw IllegalStateException("Trace is empty")
-                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
-                        val topAppBounds = Region(0, 0, dividerBounds.right,
-                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
-                        val bottomAppBounds = Region(0,
-                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
-                                displayBounds.right,
-                                displayBounds.bottom - WindowUtils.navigationBarHeight)
-                        this.hasVisibleRegion("SimpleActivity", topAppBounds)
-                                .and()
-                                .hasVisibleRegion("ImeActivity", bottomAppBounds)
-                    }
-
-                    end("appsEndingBounds", enabled = false) {
-                        val displayBounds = WindowUtils.displayBounds
-                        val entry = this.trace.entries.lastOrNull()
-                                ?: throw IllegalStateException("Trace is empty")
-                        val dividerBounds = entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
-
-                        val topAppBounds = Region(0, 0, dividerBounds.right,
-                                dividerBounds.top + WindowUtils.dockedStackDividerInset)
-                        val bottomAppBounds = Region(0,
-                                dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
-                                displayBounds.right,
-                                displayBounds.bottom - WindowUtils.navigationBarHeight)
-
-                        this.hasVisibleRegion(sSimpleActivity, topAppBounds)
-                                .and()
-                                .hasVisibleRegion(sImeActivity, bottomAppBounds)
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange()
-                }
-            }
-        }
-    }
-
+class ResizeSplitScreenTest(
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         private const val sSimpleActivity = "SimpleActivity"
         private const val sImeActivity = "ImeActivity"
-        private val rotation = Surface.ROTATION_0
         private val startRatio = Rational(1, 3)
         private val stopRatio = Rational(2, 3)
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testAppTop = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+            val testAppBottom = ImeAppHelper(instrumentation)
+
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName {
+                        val description = (startRatio.toString().replace("/", "-") + "_to_" +
+                            stopRatio.toString().replace("/", "-"))
+                        buildTestTag("resizeSplitScreen", testAppTop.launcherName,
+                            configuration.startRotation, configuration.endRotation,
+                            testAppBottom.launcherName, description)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        eachRun {
+                            device.wakeUpAndGoToHomeScreen()
+                            this.setRotation(configuration.startRotation)
+                            this.launcherStrategy.clearRecentAppsFromOverview()
+                            testAppBottom.open()
+                            device.pressHome()
+                            testAppTop.open()
+                            device.waitForIdle()
+                            device.launchSplitScreen()
+                            val snapshot =
+                                device.findObject(By.res(device.launcherPackageName, "snapshot"))
+                            snapshot.click()
+                            testAppBottom.openIME(device)
+                            device.pressBack()
+                            device.resizeSplitScreen(startRatio)
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                            device.pressHome()
+                            testAppTop.exit()
+                            testAppBottom.exit()
+                        }
+                        test {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                    }
+                    transitions {
+                        device.resizeSplitScreen(stopRatio)
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+
+                            all("topAppWindowIsAlwaysVisible", bugId = 156223549) {
+                                this.showsAppWindow(sSimpleActivity)
+                            }
+
+                            all("bottomAppWindowIsAlwaysVisible", bugId = 156223549) {
+                                this.showsAppWindow(sImeActivity)
+                            }
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation)
+                            navBarLayerRotatesAndScales(configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            all("topAppLayerIsAlwaysVisible") {
+                                this.showsLayer(sSimpleActivity)
+                            }
+
+                            all("bottomAppLayerIsAlwaysVisible") {
+                                this.showsLayer(sImeActivity)
+                            }
+
+                            all("dividerLayerIsAlwaysVisible") {
+                                this.showsLayer(DOCKED_STACK_DIVIDER)
+                            }
+
+                            start("appsStartingBounds", enabled = false) {
+                                val displayBounds = WindowUtils.displayBounds
+                                val entry = this.trace.entries.firstOrNull()
+                                    ?: throw IllegalStateException("Trace is empty")
+                                val dividerBounds =
+                                    entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+                                val topAppBounds = Region(0, 0, dividerBounds.right,
+                                    dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                                val bottomAppBounds = Region(0,
+                                    dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                    displayBounds.right,
+                                    displayBounds.bottom - WindowUtils.navigationBarHeight)
+                                this.hasVisibleRegion("SimpleActivity", topAppBounds)
+                                    .and()
+                                    .hasVisibleRegion("ImeActivity", bottomAppBounds)
+                            }
+
+                            end("appsEndingBounds", enabled = false) {
+                                val displayBounds = WindowUtils.displayBounds
+                                val entry = this.trace.entries.lastOrNull()
+                                    ?: throw IllegalStateException("Trace is empty")
+                                val dividerBounds =
+                                    entry.getVisibleBounds(DOCKED_STACK_DIVIDER).bounds
+
+                                val topAppBounds = Region(0, 0, dividerBounds.right,
+                                    dividerBounds.top + WindowUtils.dockedStackDividerInset)
+                                val bottomAppBounds = Region(0,
+                                    dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
+                                    displayBounds.right,
+                                    displayBounds.bottom - WindowUtils.navigationBarHeight)
+
+                                this.hasVisibleRegion(sSimpleActivity, topAppBounds)
+                                    .and()
+                                    .hasVisibleRegion(sImeActivity, bottomAppBounds)
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange()
+                        }
+                    }
+                }
+        }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
index 7447bda..f966a66 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/SplitScreenToLauncherTest.kt
@@ -19,23 +19,29 @@
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.NonRotationTestBase
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
+import com.android.server.wm.flicker.Flicker
+import com.android.server.wm.flicker.FlickerTestRunner
+import com.android.server.wm.flicker.FlickerTestRunnerFactory
 import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.focusDoesNotChange
+import com.android.server.wm.flicker.helpers.buildTestTag
 import com.android.server.wm.flicker.helpers.exitSplitScreen
 import com.android.server.wm.flicker.helpers.isInSplitScreen
 import com.android.server.wm.flicker.helpers.launchSplitScreen
+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.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
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -49,82 +55,80 @@
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 class SplitScreenToLauncherTest(
-    rotationName: String,
-    rotation: Int
-) : NonRotationTestBase(rotationName, rotation) {
-    @Test
-    fun test() {
-        val testApp = StandardAppHelper(instrumentation,
-                "com.android.server.wm.flicker.testapp", "SimpleApp")
-
-        flicker(instrumentation) {
-            withTag { buildTestTag("splitScreenToLauncher", testApp, rotation) }
-            repeat { 1 }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.open()
-                    this.setRotation(rotation)
-                    device.launchSplitScreen()
-                    device.waitForIdle()
-                }
-            }
-            teardown {
-                eachRun {
-                    testApp.exit()
-                }
-                test {
-                    if (device.isInSplitScreen()) {
-                        device.exitSplitScreen()
-                    }
-                }
-            }
-            transitions {
-                device.exitSplitScreen()
-            }
-            assertions {
-                windowManagerTrace {
-                    navBarWindowIsAlwaysVisible()
-                    statusBarWindowIsAlwaysVisible()
-                }
-
-                layersTrace {
-                    navBarLayerIsAlwaysVisible()
-                    statusBarLayerIsAlwaysVisible()
-                    noUncoveredRegions(rotation)
-                    navBarLayerRotatesAndScales(rotation)
-                    statusBarLayerRotatesScales(rotation)
-
-                    // b/161435597 causes the test not to work on 90 degrees
-                    all("dividerLayerBecomesInvisible") {
-                        this.showsLayer(DOCKED_STACK_DIVIDER)
-                                .then()
-                                .hidesLayer(DOCKED_STACK_DIVIDER)
-                    }
-
-                    all("appLayerBecomesInvisible") {
-                        this.showsLayer(testApp.getPackage())
-                            .then()
-                            .hidesLayer(testApp.getPackage())
-                    }
-                }
-
-                eventLog {
-                    focusDoesNotChange(bugId = 151179149)
-                }
-            }
-        }
-    }
-
+    testName: String,
+    flickerSpec: Flicker
+) : FlickerTestRunner(testName, flickerSpec) {
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<Array<Any>> {
+            val instrumentation = InstrumentationRegistry.getInstrumentation()
+            val testApp = StandardAppHelper(instrumentation,
+                "com.android.server.wm.flicker.testapp", "SimpleApp")
+
             // b/161435597 causes the test not to work on 90 degrees
-            val supportedRotations = intArrayOf(Surface.ROTATION_0)
-            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+            return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0))
+                .buildTest { configuration ->
+                    withTestName {
+                        buildTestTag("splitScreenToLauncher", testApp, configuration)
+                    }
+                    repeat { configuration.repetitions }
+                    setup {
+                        test {
+                            device.wakeUpAndGoToHomeScreen()
+                        }
+                        eachRun {
+                            testApp.open()
+                            this.setRotation(configuration.endRotation)
+                            device.launchSplitScreen()
+                            device.waitForIdle()
+                        }
+                    }
+                    teardown {
+                        eachRun {
+                            testApp.exit()
+                        }
+                        test {
+                            if (device.isInSplitScreen()) {
+                                device.exitSplitScreen()
+                            }
+                        }
+                    }
+                    transitions {
+                        device.exitSplitScreen()
+                    }
+                    assertions {
+                        windowManagerTrace {
+                            navBarWindowIsAlwaysVisible()
+                            statusBarWindowIsAlwaysVisible()
+                        }
+
+                        layersTrace {
+                            navBarLayerIsAlwaysVisible()
+                            statusBarLayerIsAlwaysVisible()
+                            noUncoveredRegions(configuration.endRotation)
+                            navBarLayerRotatesAndScales(configuration.endRotation)
+                            statusBarLayerRotatesScales(configuration.endRotation)
+
+                            // b/161435597 causes the test not to work on 90 degrees
+                            all("dividerLayerBecomesInvisible") {
+                                this.showsLayer(DOCKED_STACK_DIVIDER)
+                                    .then()
+                                    .hidesLayer(DOCKED_STACK_DIVIDER)
+                            }
+
+                            all("appLayerBecomesInvisible") {
+                                this.showsLayer(testApp.getPackage())
+                                    .then()
+                                    .hidesLayer(testApp.getPackage())
+                            }
+                        }
+
+                        eventLog {
+                            focusDoesNotChange(bugId = 151179149)
+                        }
+                    }
+                }
         }
     }
 }
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 226d2d5..9bf6b6f 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -272,6 +274,8 @@
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/rollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         after.forEach(dir -> assertDirectoryIsEmpty(dir));
     }
 
@@ -358,6 +362,8 @@
         List<String> after = getSnapshotDirectories("/data/misc/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         after.forEach(dir -> assertDirectoryIsEmpty(dir));
     }
 
@@ -405,6 +411,8 @@
         List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         after.forEach(dir -> assertDirectoryIsEmpty(dir));
     }
 
@@ -450,6 +458,8 @@
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         after.forEach(dir -> assertDirectoryIsEmpty(dir));
     }
 
@@ -509,6 +519,8 @@
         List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback");
         // Only check directories newly created during the test
         after.removeAll(before);
+        // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test
+        assertThat(after).hasSize(1);
         // Expire all rollbacks and check CE snapshot directories are deleted
         runPhase("testCleanUp");
         for (String dir : after) {
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index 050e9c3..c30d761 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -4,9 +4,9 @@
      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.
@@ -40,7 +40,14 @@
             android:label="Glow Examples"/>
 
         <activity android:name=".materials.GlassActivity"
-            android:label="Glass Examples"/>
+            android:label="Glass Examples"
+            android:banner="@drawable/background1"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
+            </intent-filter>
+        </activity>
 
     </application>
 </manifest>
diff --git a/tests/SilkFX/res/layout-television/activity_glass.xml b/tests/SilkFX/res/layout-television/activity_glass.xml
new file mode 100644
index 0000000..1f566860
--- /dev/null
+++ b/tests/SilkFX/res/layout-television/activity_glass.xml
@@ -0,0 +1,302 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+
+    <ImageView
+        android:id="@+id/background"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:scaleType="matrix"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:srcCompat="@drawable/background1" />
+
+    <com.android.test.silkfx.materials.GlassView
+        android:id="@+id/materialView"
+        android:layout_width="400dp"
+        android:layout_height="100dp"
+        android:layout_marginEnd="64dp"
+        android:layout_marginStart="64dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@id/background"
+        app:layout_constraintStart_toStartOf="@id/background"
+        app:layout_constraintTop_toTopOf="parent">
+        <TextView
+            android:id="@+id/textOverlay"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="18dp"
+            android:layout_gravity="center"
+            android:textColor="#ffffff"
+            android:text="Lorem Ipsum dolor sit amet." />
+    </com.android.test.silkfx.materials.GlassView>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/bottomPanel"
+        android:layout_width="400dp"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/colorBackground"
+        android:paddingTop="24dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent">
+
+    <SeekBar
+        android:id="@+id/materialOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="12"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/zoom"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:min="-100"
+        android:max="100"
+        android:progress="-15"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadiusTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/blurRadius"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="16dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginStart="12dp"
+        android:max="150"
+        android:progress="40"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/scrimOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="16dp"
+        android:max="100"
+        android:progress="50"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacityTitle"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <SeekBar
+        android:id="@+id/noiseOpacity"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="24dp"
+        android:max="100"
+        android:progress="15"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/scrimOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Scrim Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/scrimOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/materialOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Soft light Opacity"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/materialOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/zoomTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Zoom"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/zoom"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Blur Radius"
+        android:textColor="@android:color/white"
+        app:layout_constraintBottom_toTopOf="@+id/blurRadius"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/noiseOpacityTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:textColor="@android:color/white"
+        android:text="Noise Opacity"
+        app:layout_constraintBottom_toTopOf="@+id/noiseOpacity"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <ImageView
+        android:id="@+id/background1"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="16dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toTopOf="@+id/lightMaterialSwitch"
+        app:layout_constraintStart_toStartOf="parent"
+        android:src="@drawable/background1" />
+
+    <ImageView
+        android:id="@+id/background2"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        android:scaleType="centerCrop"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background1"
+        android:src="@drawable/background2" />
+
+    <ImageView
+        android:id="@+id/background3"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onBackgroundClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background2"
+        android:src="@drawable/background3" />
+
+    <Button
+        android:id="@+id/pickImage"
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_marginStart="8dp"
+        android:scaleType="centerCrop"
+        android:foreground="?android:attr/selectableItemBackgroundBorderless"
+        android:clickable="true"
+        android:onClick="onPickImageClick"
+        app:layout_constraintBottom_toBottomOf="@+id/background1"
+        app:layout_constraintStart_toEndOf="@+id/background3"
+        android:text="Pick file" />
+
+    <Switch
+        android:id="@+id/lightMaterialSwitch"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginBottom="8dp"
+        android:text="Light Material"
+        app:layout_constraintBottom_toTopOf="@+id/zoomTitle"
+        app:layout_constraintStart_toStartOf="parent" />
+
+    <TextView
+        android:id="@+id/blurRadiusValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/blurRadiusTitle"
+        app:layout_constraintStart_toEndOf="@+id/blurRadiusTitle" />
+
+    <TextView
+        android:id="@+id/zoomValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/zoomTitle"
+        app:layout_constraintStart_toEndOf="@+id/zoomTitle" />
+
+    <TextView
+        android:id="@+id/materialOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/materialOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/materialOpacityTitle" />
+
+    <TextView
+        android:id="@+id/noiseOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/noiseOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/noiseOpacityTitle" />
+
+
+    <TextView
+        android:id="@+id/scrimOpacityValue"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="TextView"
+        android:layout_marginLeft="8dp"
+        app:layout_constraintBottom_toBottomOf="@+id/scrimOpacityTitle"
+        app:layout_constraintStart_toEndOf="@+id/scrimOpacityTitle" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index c895420..85704d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -213,7 +213,7 @@
 
     public void connect() {
         assertNotEquals("MockNetworkAgents can only be connected once",
-                getNetworkInfo().getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
+                mNetworkInfo.getDetailedState(), NetworkInfo.DetailedState.CONNECTED);
         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
         mNetworkAgent.sendNetworkInfo(mNetworkInfo);
     }
@@ -268,10 +268,6 @@
         return mNetworkAgent;
     }
 
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
-
     public NetworkCapabilities getNetworkCapabilities() {
         return mNetworkCapabilities;
     }
diff --git a/tests/net/java/android/net/util/IpUtilsTest.java b/tests/net/java/android/net/util/IpUtilsTest.java
deleted file mode 100644
index 193d85d..0000000
--- a/tests/net/java/android/net/util/IpUtilsTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static org.junit.Assert.assertEquals;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.nio.ByteBuffer;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class IpUtilsTest {
-
-    private static final int IPV4_HEADER_LENGTH = 20;
-    private static final int IPV6_HEADER_LENGTH = 40;
-    private static final int TCP_HEADER_LENGTH = 20;
-    private static final int UDP_HEADER_LENGTH = 8;
-    private static final int IP_CHECKSUM_OFFSET = 10;
-    private static final int TCP_CHECKSUM_OFFSET = 16;
-    private static final int UDP_CHECKSUM_OFFSET = 6;
-
-    private int getUnsignedByte(ByteBuffer buf, int offset) {
-        return buf.get(offset) & 0xff;
-    }
-
-    private int getChecksum(ByteBuffer buf, int offset) {
-        return getUnsignedByte(buf, offset) * 256 + getUnsignedByte(buf, offset + 1);
-    }
-
-    private void assertChecksumEquals(int expected, short actual) {
-        assertEquals(Integer.toHexString(expected), Integer.toHexString(actual & 0xffff));
-    }
-
-    // Generate test packets using Python code like this::
-    //
-    // from scapy import all as scapy
-    //
-    // def JavaPacketDefinition(bytes):
-    //   out = "        ByteBuffer packet = ByteBuffer.wrap(new byte[] {\n            "
-    //   for i in xrange(len(bytes)):
-    //     out += "(byte) 0x%02x" % ord(bytes[i])
-    //     if i < len(bytes) - 1:
-    //       if i % 4 == 3:
-    //         out += ",\n            "
-    //       else:
-    //         out += ", "
-    //   out += "\n        });"
-    //   return out
-    //
-    // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2") /
-    //           scapy.UDP(sport=12345, dport=7) /
-    //           "hello")
-    // print JavaPacketDefinition(str(packet))
-
-    @Test
-    public void testIpv6TcpChecksum() throws Exception {
-        // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80) /
-        //           scapy.TCP(sport=12345, dport=7,
-        //                     seq=1692871236, ack=128376451, flags=16,
-        //                     window=32768) /
-        //           "hello, world")
-        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
-            (byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x20, (byte) 0x06, (byte) 0x40,
-            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
-            (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
-            (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
-            (byte) 0x30, (byte) 0x39, (byte) 0x00, (byte) 0x07,
-            (byte) 0x64, (byte) 0xe7, (byte) 0x2a, (byte) 0x44,
-            (byte) 0x07, (byte) 0xa6, (byte) 0xde, (byte) 0x83,
-            (byte) 0x50, (byte) 0x10, (byte) 0x80, (byte) 0x00,
-            (byte) 0xee, (byte) 0x71, (byte) 0x00, (byte) 0x00,
-            (byte) 0x68, (byte) 0x65, (byte) 0x6c, (byte) 0x6c,
-            (byte) 0x6f, (byte) 0x2c, (byte) 0x20, (byte) 0x77,
-            (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64
-        });
-
-        // Check that a valid packet has checksum 0.
-        int transportLen = packet.limit() - IPV6_HEADER_LENGTH;
-        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-
-        // Check that we can calculate the checksum from scratch.
-        int sumOffset = IPV6_HEADER_LENGTH + TCP_CHECKSUM_OFFSET;
-        int sum = getUnsignedByte(packet, sumOffset) * 256 + getUnsignedByte(packet, sumOffset + 1);
-        assertEquals(0xee71, sum);
-
-        packet.put(sumOffset, (byte) 0);
-        packet.put(sumOffset + 1, (byte) 0);
-        assertChecksumEquals(sum, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-
-        // Check that writing the checksum back into the packet results in a valid packet.
-        packet.putShort(
-            sumOffset,
-            IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-        assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
-    }
-
-    @Test
-    public void testIpv4UdpChecksum() {
-        // packet = (scapy.IP(src="192.0.2.1", dst="192.0.2.2", tos=0x40) /
-        //           scapy.UDP(sport=32012, dport=4500) /
-        //           "\xff")
-        ByteBuffer packet = ByteBuffer.wrap(new byte[] {
-            (byte) 0x45, (byte) 0x40, (byte) 0x00, (byte) 0x1d,
-            (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
-            (byte) 0x40, (byte) 0x11, (byte) 0xf6, (byte) 0x8b,
-            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x01,
-            (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x02,
-            (byte) 0x7d, (byte) 0x0c, (byte) 0x11, (byte) 0x94,
-            (byte) 0x00, (byte) 0x09, (byte) 0xee, (byte) 0x36,
-            (byte) 0xff
-        });
-
-        // Check that a valid packet has IP checksum 0 and UDP checksum 0xffff (0 is not a valid
-        // UDP checksum, so the udpChecksum rewrites 0 to 0xffff).
-        assertEquals(0, IpUtils.ipChecksum(packet, 0));
-        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-
-        // Check that we can calculate the checksums from scratch.
-        final int ipSumOffset = IP_CHECKSUM_OFFSET;
-        final int ipSum = getChecksum(packet, ipSumOffset);
-        assertEquals(0xf68b, ipSum);
-
-        packet.put(ipSumOffset, (byte) 0);
-        packet.put(ipSumOffset + 1, (byte) 0);
-        assertChecksumEquals(ipSum, IpUtils.ipChecksum(packet, 0));
-
-        final int udpSumOffset = IPV4_HEADER_LENGTH + UDP_CHECKSUM_OFFSET;
-        final int udpSum = getChecksum(packet, udpSumOffset);
-        assertEquals(0xee36, udpSum);
-
-        packet.put(udpSumOffset, (byte) 0);
-        packet.put(udpSumOffset + 1, (byte) 0);
-        assertChecksumEquals(udpSum, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-
-        // Check that writing the checksums back into the packet results in a valid packet.
-        packet.putShort(ipSumOffset, IpUtils.ipChecksum(packet, 0));
-        packet.putShort(udpSumOffset, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-        assertEquals(0, IpUtils.ipChecksum(packet, 0));
-        assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
-    }
-}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 7ab4d97..bc3db11 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1155,7 +1155,7 @@
                     new String[] { EGRESS_IFACE, "l2tp", expectedAddr, "1701", profile.l2tpSecret,
                             "name", profile.username, "password", profile.password,
                             "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
-                            "idle", "1800", "mtu", "1400", "mru", "1400" },
+                            "idle", "1800", "mtu", "1270", "mru", "1270" },
                     deps.mtpdArgs.get(10, TimeUnit.SECONDS));
             // Now wait for the runner to be ready before testing for the route.
             legacyRunnerReady.block(10_000);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 8f09377..6d2c7dc 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -150,7 +151,7 @@
     }
 
     private void assertRatTypeChangedForSub(String subscriberId, int ratType) {
-        assertEquals(mMonitor.getRatTypeForSubscriberId(subscriberId), ratType);
+        assertEquals(ratType, mMonitor.getRatTypeForSubscriberId(subscriberId));
         final ArgumentCaptor<Integer> typeCaptor = ArgumentCaptor.forClass(Integer.class);
         // Verify callback with the subscriberId and the RAT type should be as expected.
         // It will fail if get a callback with an unexpected RAT type.
@@ -302,26 +303,84 @@
         reset(mDelegate);
 
         // Set IMSI to null again to simulate somehow IMSI is not available, such as
-        // modem crash. Verify service should not unregister listener.
+        // modem crash. Verify service should unregister listener.
         updateSubscriberIdForTestSub(TEST_SUBID1, null);
-        verify(mTelephonyManager, never()).listen(any(), anyInt());
-        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
+                eq(PhoneStateListener.LISTEN_NONE));
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
         reset(mDelegate);
+        clearInvocations(mTelephonyManager);
 
-        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
-        // is not available. The monitor keeps the listener even if the IMSI disappears because
-        // the IMSI can never change for any given subId, therefore even if the IMSI is updated
-        // to null, the monitor should continue accepting updates of the RAT type. However,
-        // telephony is never actually supposed to do this, if the IMSI disappears there should
-        // not be updates, but it's still the right thing to do theoretically.
-        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+        // Simulate somehow IMSI is back. Verify service will register with
+        // another listener and fire callback accordingly.
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        reset(mDelegate);
+        clearInvocations(mTelephonyManager);
+
+        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 still works.
+        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
                 TelephonyManager.NETWORK_TYPE_LTE);
         assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
         reset(mDelegate);
 
         mMonitor.stop();
+        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor2.getValue()),
+                eq(PhoneStateListener.LISTEN_NONE));
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
+
+    /**
+     * Verify that when IMSI suddenly changed for a given subId, the service will register a new
+     * listener and unregister the old one, and report changes on updated IMSI. This is for modem
+     * feature that may be enabled for certain carrier, which changes to use a different IMSI while
+     * roaming on certain networks for multi-IMSI SIM cards, but the subId stays the same.
+     */
+    @Test
+    public void testSubscriberIdChanged() {
+        mMonitor.start();
+        // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+        // before changing RAT type.
+        addTestSub(TEST_SUBID1, TEST_IMSI1);
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+        // Set RAT type of sim1 to UMTS.
+        // Verify RAT type of sim1 changes accordingly.
+        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
+        clearInvocations(mTelephonyManager);
+
+        // Simulate IMSI of sim1 changed to IMSI2. Verify the service will register with
+        // another listener and remove the old one. The RAT type of new IMSI stays at
+        // NETWORK_TYPE_UNKNOWN until received initial callback from telephony.
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor2 =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI2);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor2.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
         verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
                 eq(PhoneStateListener.LISTEN_NONE));
         assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        assertRatTypeNotChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        reset(mDelegate);
+
+        // Set RAT type of sim1 to UMTS for new listener to simulate the initial callback received
+        // from telephony after registration. Verify RAT type of sim1 changes with IMSI2
+        // accordingly.
+        setRatTypeForSub(ratTypeListenerCaptor2.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        assertRatTypeChangedForSub(TEST_IMSI2, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
     }
 }
diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS
new file mode 100644
index 0000000..33b9f0f
--- /dev/null
+++ b/tests/vcn/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+benedictwong@google.com
+ckesting@google.com
+evitayan@google.com
+nharold@google.com
+jchalard@google.com
\ No newline at end of file
diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt
index e253ae2..eef08b5 100644
--- a/wifi/jarjar-rules.txt
+++ b/wifi/jarjar-rules.txt
@@ -70,7 +70,6 @@
 rule android.net.util.InterfaceParams* com.android.wifi.x.@0
 rule android.net.util.SharedLog* com.android.wifi.x.@0
 rule android.net.util.NetUtils* com.android.wifi.x.@0
-rule android.net.util.IpUtils* com.android.wifi.x.@0
 
 rule androidx.annotation.** com.android.wifi.x.@0
 
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 7d7d2ba..7877ea1 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -87,12 +87,12 @@
         }
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -111,12 +111,12 @@
         assertThat(original.getMaxNumberOfClients()).isEqualTo(0);
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -159,12 +159,12 @@
         }
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -185,12 +185,12 @@
 
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
@@ -212,12 +212,12 @@
 
 
         SoftApConfiguration unparceled = parcelUnparcel(original);
-        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isNotSameInstanceAs(original);
         assertThat(unparceled).isEqualTo(original);
         assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
 
         SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
-        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isNotSameInstanceAs(original);
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }