Merge "Migrate existing Build.VERSION.SDK_INT checks to SdkLevelUtil"
diff --git a/Android.bp b/Android.bp
index 5af7756..06f3666 100644
--- a/Android.bp
+++ b/Android.bp
@@ -330,6 +330,7 @@
         ":gatekeeper_aidl",
         ":gsiservice_aidl",
         ":incidentcompanion_aidl",
+        ":inputconstants_aidl",
         ":installd_aidl",
         ":keystore_aidl",
         ":libaudioclient_aidl",
@@ -598,6 +599,12 @@
         "//frameworks/base/apex/jobscheduler/framework",
         "//frameworks/base/packages/Tethering/tests/unit",
     ],
+    errorprone: {
+        javacflags: [
+            "-Xep:AndroidFrameworkCompatChange:ERROR",
+            "-Xep:AndroidFrameworkUid:ERROR",
+        ],
+    },
 }
 
 // This "framework" module is NOT installed to the device. It's
diff --git a/StubLibraries.bp b/StubLibraries.bp
index a3a2094..53053ce 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -58,7 +58,24 @@
             "media/aidl",
         ],
     },
-    libs: ["framework-internal-utils"],
+    // These are libs from framework-internal-utils that are required (i.e. being referenced)
+    // from framework-non-updatable-sources. Add more here when there's a need.
+    // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+    // dependencies gets bigger.
+    libs: [
+        "android.hardware.cas-V1.2-java",
+        "android.hardware.health-V1.0-java-constants",
+        "android.hardware.radio-V1.5-java",
+        "android.hardware.thermal-V1.0-java-constants",
+        "android.hardware.thermal-V2.0-java",
+        "android.hardware.tv.input-V1.0-java-constants",
+        "android.hardware.tv.tuner-V1.0-java-constants",
+        "android.hardware.usb-V1.0-java-constants",
+        "android.hardware.usb-V1.1-java-constants",
+        "android.hardware.usb.gadget-V1.0-java",
+        "android.hardware.vibrator-V1.3-java",
+        "framework-protos",
+    ],
     installable: false,
     annotations_enabled: true,
     previous_api: ":android.api.public.latest",
@@ -143,6 +160,11 @@
             api_file: "non-updatable-api/current.txt",
             removed_api_file: "non-updatable-api/removed.txt",
         },
+        last_released: {
+            api_file: ":android-non-updatable.api.public.latest",
+            removed_api_file: ":android-non-updatable-removed.api.public.latest",
+            baseline_file: ":public-api-incompatibilities-with-last-released",
+        },
         api_lint: {
             enabled: true,
             new_since: ":android-non-updatable.api.public.latest",
@@ -205,6 +227,11 @@
             api_file: "non-updatable-api/system-current.txt",
             removed_api_file: "non-updatable-api/system-removed.txt",
         },
+        last_released: {
+            api_file: ":android-non-updatable.api.system.latest",
+            removed_api_file: ":android-non-updatable-removed.api.system.latest",
+            baseline_file: ":system-api-incompatibilities-with-last-released"
+        },
         api_lint: {
             enabled: true,
             new_since: ":android-non-updatable.api.system.latest",
diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
index bb6b691..66b2b0e 100644
--- a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
+++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
@@ -53,19 +53,19 @@
         final String text = mTextUtil.nextRandomParagraph(
                 WORD_LENGTH, 4 * 1024 * 1024 /* 4mb text */).toString();
         final RenderNode node = RenderNode.create("benchmark", null);
-        final RenderNode child = RenderNode.create("child", null);
-        child.setLeftTopRightBottom(50, 50, 100, 100);
-
-        RecordingCanvas canvas = node.start(100, 100);
-        node.end(canvas);
-        canvas = child.start(50, 50);
-        child.end(canvas);
 
         final Random r = new Random(0);
-
         while (state.keepRunning()) {
+            state.pauseTiming();
+            RecordingCanvas canvas = node.beginRecording();
             int start = r.nextInt(text.length() - 100);
+            state.resumeTiming();
+
             canvas.drawText(text, start, start + 100, 0, 0, PAINT);
+
+            state.pauseTiming();
+            node.endRecording();
+            state.resumeTiming();
         }
     }
 }
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index efcabd8..833cc0f 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -226,7 +226,7 @@
             mMeasuredTimeNs = 0;
 
             final long startTime = SystemClock.elapsedRealtimeNanos();
-            atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
+            atm.startRecentsActivity(sRecentsIntent, 0 /* eventTime */, anim);
             final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
             mMeasuredTimeNs += elapsedTimeNsOfStart;
             state.addExtraResult("start", elapsedTimeNsOfStart);
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
index d955289..a9d5716 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -115,7 +115,7 @@
         runWithShellPermissionIdentity(() -> {
             final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
             atm.removeAllVisibleRecentTasks();
-            atm.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
+            atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
                     ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
         });
         PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
index d2826d0..2b69863 100644
--- a/apex/media/framework/api/module-lib-current.txt
+++ b/apex/media/framework/api/module-lib-current.txt
@@ -1,14 +1,14 @@
 // Signature format: 2.0
 package android.media {
 
-  public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
-    ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
-    method public int describeContents();
-    method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
-    method public java.util.List<T> getList();
-    method public void setInlineCountLimit(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
+  @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();
+    method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+    method @Deprecated public java.util.List<T> getList();
+    method @Deprecated public void setInlineCountLimit(int);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
   }
 
 }
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
index e1223f6..47ac193 100644
--- a/apex/media/framework/java/android/media/MediaParceledListSlice.java
+++ b/apex/media/framework/java/android/media/MediaParceledListSlice.java
@@ -17,7 +17,6 @@
 package android.media;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -32,13 +31,16 @@
  * Transfer a large list of Parcelable objects across an IPC.  Splits into
  * multiple transactions if needed.
  *
- * @see BaseMediaParceledListSlice
- *
  * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved
  *       to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API
  *       for moving classes step by step.
+ *
+ * @param <T> The type of the elements in the list.
+ * @see BaseMediaParceledListSlice
+ * @deprecated This is temporary marked as @SystemApi. Should be removed from the API surface.
  * @hide
  */
+@Deprecated
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class MediaParceledListSlice<T extends Parcelable>
         extends BaseMediaParceledListSlice<T> {
diff --git a/api/Android.bp b/api/Android.bp
index 490c980..e403082 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -64,6 +64,24 @@
 }
 
 genrule {
+    name: "frameworks-base-api-removed-merged.txt",
+    srcs: [
+        ":conscrypt.module.public.api{.public.removed-api.txt}",
+        ":framework-media{.public.removed-api.txt}",
+        ":framework-mediaprovider{.public.removed-api.txt}",
+        ":framework-permission{.public.removed-api.txt}",
+        ":framework-sdkextensions{.public.removed-api.txt}",
+        ":framework-statsd{.public.removed-api.txt}",
+        ":framework-tethering{.public.removed-api.txt}",
+        ":framework-wifi{.public.removed-api.txt}",
+        ":non-updatable-removed.txt",
+    ],
+    out: ["removed.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
     name: "frameworks-base-api-system-current-merged.txt",
     srcs: [
         ":framework-graphics{.system.api.txt}",
@@ -82,6 +100,23 @@
 }
 
 genrule {
+    name: "frameworks-base-api-system-removed-merged.txt",
+    srcs: [
+        ":framework-media{.system.removed-api.txt}",
+        ":framework-mediaprovider{.system.removed-api.txt}",
+        ":framework-permission{.system.removed-api.txt}",
+        ":framework-sdkextensions{.system.removed-api.txt}",
+        ":framework-statsd{.system.removed-api.txt}",
+        ":framework-tethering{.system.removed-api.txt}",
+        ":framework-wifi{.system.removed-api.txt}",
+        ":non-updatable-system-removed.txt",
+    ],
+    out: ["system-removed.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
     name: "frameworks-base-api-module-lib-current-merged.txt",
     srcs: [
         ":framework-graphics{.module-lib.api.txt}",
@@ -98,3 +133,20 @@
     tools: ["metalava"],
     cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
 }
+
+genrule {
+    name: "frameworks-base-api-module-lib-removed-merged.txt",
+    srcs: [
+        ":framework-media{.module-lib.removed-api.txt}",
+        ":framework-mediaprovider{.module-lib.removed-api.txt}",
+        ":framework-permission{.module-lib.removed-api.txt}",
+        ":framework-sdkextensions{.module-lib.removed-api.txt}",
+        ":framework-statsd{.module-lib.removed-api.txt}",
+        ":framework-tethering{.module-lib.removed-api.txt}",
+        ":framework-wifi{.module-lib.removed-api.txt}",
+        ":non-updatable-module-lib-removed.txt",
+    ],
+    out: ["module-lib-removed.txt"],
+    tools: ["metalava"],
+    cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
diff --git a/api/current.txt b/api/current.txt
index 94e90a7..153cf69 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11454,10 +11454,10 @@
 
   public final class ApkChecksum implements android.os.Parcelable {
     method public int describeContents();
-    method public int getKind();
-    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
-    method @Nullable public String getSourcePackageName();
+    method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getInstallerPackageName();
     method @Nullable public String getSplitName();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
@@ -11567,17 +11567,17 @@
   public final class Checksum implements android.os.Parcelable {
     ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
-    method public int getKind();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
-    field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
-    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
-    field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
-    field public static final int WHOLE_SHA256 = 8; // 0x8
-    field public static final int WHOLE_SHA512 = 16; // 0x10
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
+    field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2
+    field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4
+    field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8
+    field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10
   }
 
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
@@ -12027,7 +12027,6 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
-    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12100,6 +12099,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 3801e1d..19f348c 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -53,14 +53,14 @@
     field public static final int FLAG_FROM_KEY = 4096; // 0x1000
   }
 
-  public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
-    ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
-    method public int describeContents();
-    method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
-    method public java.util.List<T> getList();
-    method public void setInlineCountLimit(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
+  @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();
+    method @Deprecated @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+    method @Deprecated public java.util.List<T> getList();
+    method @Deprecated public void setInlineCountLimit(int);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 325175b..8979471 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -126,6 +126,7 @@
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
+    field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
@@ -306,6 +307,7 @@
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemGallery = 17039399; // 0x1040027
+    field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
 
   public static final class R.style {
@@ -1390,6 +1392,57 @@
 
 }
 
+package android.app.time {
+
+  public final class TimeManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void removeTimeZoneDetectorListener(@NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull android.app.time.TimeZoneConfiguration);
+  }
+
+  @java.lang.FunctionalInterface public static interface TimeManager.TimeZoneDetectorListener {
+    method public void onChange();
+  }
+
+  public final class TimeZoneCapabilities implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getConfigureAutoDetectionEnabledCapability();
+    method public int getConfigureGeoDetectionEnabledCapability();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14
+    field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e
+    field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa
+    field public static final int CAPABILITY_POSSESSED = 40; // 0x28
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR;
+  }
+
+  public final class TimeZoneCapabilitiesAndConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.app.time.TimeZoneCapabilities getCapabilities();
+    method @NonNull public android.app.time.TimeZoneConfiguration getConfiguration();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilitiesAndConfig> CREATOR;
+  }
+
+  public final class TimeZoneConfiguration implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isAutoDetectionEnabled();
+    method public boolean isGeoDetectionEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneConfiguration> CREATOR;
+  }
+
+  public static final class TimeZoneConfiguration.Builder {
+    ctor public TimeZoneConfiguration.Builder();
+    ctor public TimeZoneConfiguration.Builder(@NonNull android.app.time.TimeZoneConfiguration);
+    method @NonNull public android.app.time.TimeZoneConfiguration build();
+    method @NonNull public android.app.time.TimeZoneConfiguration.Builder setAutoDetectionEnabled(boolean);
+    method @NonNull public android.app.time.TimeZoneConfiguration.Builder setGeoDetectionEnabled(boolean);
+  }
+
+}
+
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
@@ -4118,7 +4171,7 @@
     method @Deprecated @NonNull public String getProvider();
     method public int getQuality();
     method @Deprecated public float getSmallestDisplacement();
-    method @Nullable public android.os.WorkSource getWorkSource();
+    method @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
diff --git a/api/test-current.txt b/api/test-current.txt
index 68d2823..25a4710 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -45,6 +45,7 @@
     field public static final int config_defaultDialer = 17039395; // 0x1040023
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemGallery = 17039399; // 0x1040027
+    field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
 
 }
@@ -135,12 +136,12 @@
   public class ActivityTaskManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void clearLaunchParamsForPackages(java.util.List<java.lang.String>);
     method public static boolean currentUiModeSupportsErrorDialogs(@NonNull android.content.Context);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToStack(int, int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedStack(int, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void moveTaskToRootTask(int, int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean moveTopActivityToPinnedRootTask(int, @NonNull android.graphics.Rect);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
@@ -1452,6 +1453,18 @@
 
 }
 
+package android.hardware.input {
+
+  public final class InputManager {
+    method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
+    method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float);
+    field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
+  }
+
+}
+
 package android.hardware.lights {
 
   public final class Light implements android.os.Parcelable {
@@ -1716,7 +1729,7 @@
   }
 
   public final class LocationRequest implements android.os.Parcelable {
-    method @Nullable public android.os.WorkSource getWorkSource();
+    method @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 42b560d..bda9e24 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3777,6 +3777,7 @@
         LAUNCHER = 1;
         NOTIFICATION = 2;
         LOCKSCREEN = 3;
+        RECENTS_ANIMATION = 4;
     }
     // The type of the startup source.
     optional SourceType source_type = 16;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 3dbb6ed..a8ef54a 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 
 #include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 
@@ -122,6 +123,47 @@
     VLOG("~CountMetricProducer() called");
 }
 
+bool CountMetricProducer::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 CountMetric& metric = config.count_metric(configIndex);
+    int trackerIndex;
+    // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    return true;
+}
+
 void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                                          const HashableDimensionKey& primaryKey,
                                          const FieldValue& oldState, const FieldValue& newState) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 6b2f2ca..0769ac4 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -97,6 +97,22 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    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;
+
     std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket (may be a partial bucket).
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 320b285..4360010 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -566,7 +566,9 @@
     FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
 
     FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
     FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
 };
 
 }  // namespace statsd
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 12d3b94..5ff6975 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -587,6 +587,51 @@
 
     // 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);
+        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,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                newMetricProducers.push_back(producer);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                sp<MetricProducer> 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: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+    }
     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);
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 51df7df..98c6222 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -348,6 +348,81 @@
     return true;
 }
 
+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,
+        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 unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        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 CountMetric \"%lld\"", (long long)metric.id());
+        return nullptr;
+    }
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullptr;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullptr;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullptr;
+        }
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullptr;
+        }
+    } else {
+        if (metric.state_link_size() > 0) {
+            ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+            return nullptr;
+        }
+    }
+
+    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 nullptr;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullptr;
+    }
+
+    return new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                   metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+                                   eventDeactivationMap, slicedStateAtoms, stateGroupMap);
+}
+
 sp<MetricProducer> createEventMetricProducerAndUpdateMetadata(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
@@ -550,68 +625,20 @@
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
     for (int i = 0; i < config.count_metric_size(); i++) {
-        const CountMetric& metric = config.count_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
         int metricIndex = allMetricProducers.size();
+        const CountMetric& metric = config.count_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;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
-                                            metric.links(), allConditionTrackers, conditionIndex,
-                                            conditionToMetricMap)) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
-                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,
+        sp<MetricProducer> producer = createCountMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, 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 == nullptr) {
             return false;
         }
-
-        sp<MetricProducer> countProducer = new CountMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, metricHash,
-                timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
-                slicedStateAtoms, stateGroupMap);
-        allMetricProducers.push_back(countProducer);
+        allMetricProducers.push_back(producer);
     }
 
     // build DurationMetricProducer
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 fdfe5cf..f6b2b7d 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -93,6 +93,27 @@
         std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
         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(
+        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,
+        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 std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        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);
+
+// 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(
         const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
         const EventMetric& metric, const int metricIndex,
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 3b346c1..9e675878 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
@@ -97,6 +97,7 @@
         metricsWithActivation.clear();
         oldStateHashes.clear();
         noReportMetricIds.clear();
+        StateManager::getInstance().clear();
     }
 };
 
@@ -121,6 +122,21 @@
     return metric;
 }
 
+CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition,
+                              vector<int64_t> states) {
+    CountMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
 }  // anonymous namespace
 
 TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
@@ -1419,7 +1435,7 @@
     EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
               newMetricProducers[newMetricProducerMap.at(event1Id)]);
 
-    // Make sure replaced conditions are different and included in replacedConditions.
+    // Make sure replaced metrics are different.
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
               newMetricProducers[newMetricProducerMap.at(event2Id)]);
     EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
@@ -1478,6 +1494,239 @@
     EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
 }
 
+TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) {
+    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 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id});
+    int64_t count1Id = count1.id();
+    *config.add_count_metric() = count1;
+
+    // Will be replaced.
+    CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {});
+    int64_t count2Id = count2.id();
+    *config.add_count_metric() = count2;
+
+    // Will be replaced.
+    CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {});
+    int64_t count3Id = count3.id();
+    *config.add_count_metric() = count3;
+
+    // Will be replaced.
+    CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id});
+    int64_t count4Id = count4.id();
+    *config.add_count_metric() = count4;
+
+    // Will be deleted.
+    CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {});
+    int64_t count5Id = count5.id();
+    *config.add_count_metric() = count5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change bucket size of count2, causing it to be replaced.
+    count2.set_bucket(ONE_HOUR);
+
+    // Mark matcher 3 as replaced. Causes count3 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher3Id);
+
+    // Mark state 2 as replaced and change the state to be about a different atom.
+    // Causes count4 to be replaced.
+    set<int64_t> replacedStates;
+    replacedStates.insert(state2Id);
+    state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // Fake that predicate 1 is true for count metric 1.
+    ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id);
+    oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+    EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1);
+    // Tell the StateManager that the screen is on.
+    unique_ptr<LogEvent> event =
+            CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    StateManager::getInstance().onLogEvent(*event);
+
+    // New count metric. Should have an initial condition of true since it depends on predicate1.
+    CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id});
+    int64_t count6Id = count6.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());
+    // Fake that predicate1 is true for all new metrics.
+    vector<ConditionState> conditionCache = {ConditionState::kTrue};
+
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = count6;
+    const int count6Index = 0;
+    *newConfig.add_count_metric() = count3;
+    const int count3Index = 1;
+    *newConfig.add_count_metric() = count1;
+    const int count1Index = 2;
+    *newConfig.add_count_metric() = count4;
+    const int count4Index = 3;
+    *newConfig.add_count_metric() = count2;
+    const int count2Index = 4;
+
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+    EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // 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 = {
+            {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
+            {count4Id, count4Index}, {count6Id, count6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)],
+              newMetricProducers[newMetricProducerMap.at(count1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)],
+              newMetricProducers[newMetricProducerMap.at(count2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)],
+              newMetricProducers[newMetricProducerMap.at(count3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)],
+              newMetricProducers[newMetricProducerMap.at(count4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index));
+
+    // 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.
+    EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id);
+    EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id);
+    EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id);
+    EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id);
+    EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id);
+    EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+
+    oldMetricProducers.clear();
+    // Ensure that the screen state StateTracker did not get deleted and replaced.
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2);
+    FieldValue screenState;
+    StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY,
+                                              &screenState);
+    EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+}
+
 TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
     StatsdConfig config;
     // Add atom matchers
@@ -1605,6 +1854,135 @@
     EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
                 UnorderedElementsAre(matcher4Activation));
 }
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) {
+    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;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {});
+    int64_t countMetricId = countMetric.id();
+    *config.add_count_metric() = countMetric;
+
+    // Will be replaced since matcher2 is replaced.
+    EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt);
+    int64_t eventMetricId = eventMetric.id();
+    *config.add_event_metric() = eventMetric;
+
+    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);
+
+    // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher2Id);
+
+    // 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;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(2);
+    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());
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = countMetric;
+    const int countMetricIndex = 0;
+    *newConfig.add_event_metric() = eventMetric;
+    const int eventMetricIndex = 1;
+
+    // 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 = {
+            {countMetricId, countMetricIndex},
+            {eventMetricId, eventMetricIndex},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 2);
+    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)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(countMetricIndex));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 2);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex));
+
+    // 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 are correct.
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 3);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a61159a..85cb120 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1910,6 +1910,8 @@
         public static final int TYPE_NOTIFICATION = 2;
         /** Launched from lockscreen, including notification while the device is locked. */
         public static final int TYPE_LOCKSCREEN = 3;
+        /** Launched from recents gesture handler. */
+        public static final int TYPE_RECENTS_ANIMATION = 4;
 
         @IntDef(flag = true, prefix = { "TYPE_" }, value = {
                 TYPE_LAUNCHER,
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 3e4d5ee..d0a0e30 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -211,23 +211,23 @@
     }
 
     /**
-     * Removes stacks in the windowing modes from the system if they are of activity type
+     * Removes root tasks in the windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
+    public void removeRootTasksInWindowingModes(@NonNull int[] windowingModes) {
         try {
-            getService().removeStacksInWindowingModes(windowingModes);
+            getService().removeRootTasksInWindowingModes(windowingModes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** Removes stack of the activity types from the system. */
+    /** Removes root tasks of the activity types from the system. */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
+    public void removeRootTasksWithActivityTypes(@NonNull int[] activityTypes) {
         try {
-            getService().removeStacksWithActivityTypes(activityTypes);
+            getService().removeRootTasksWithActivityTypes(activityTypes);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -310,15 +310,15 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds Bounds to use for pinned stack.
-     * @return True if the top activity of stack was successfully moved to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds Bounds to use for pinned root task.
+     * @return True if the top activity of root task was successfully moved to the pinned root task.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, @NonNull Rect bounds) {
         try {
-            return getService().moveTopActivityToPinnedStack(stackId, bounds);
+            return getService().moveTopActivityToPinnedRootTask(rootTaskId, bounds);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -350,15 +350,15 @@
     }
 
     /**
-     * Move task to stack with given id.
+     * Move task to root task with given id.
      * @param taskId Id of the task to move.
-     * @param stackId Id of the stack for task moving.
+     * @param rootTaskId Id of the rootTask for task moving.
      * @param toTop Whether the given task should shown to top of stack.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
         try {
-            getService().moveTaskToStack(taskId, stackId, toTop);
+            getService().moveTaskToRootTask(taskId, rootTaskId, toTop);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -380,13 +380,13 @@
 
     /**
      * Resize docked stack & its task to given stack & task bounds.
-     * @param stackBounds Bounds to resize stack.
+     * @param rootTaskBounds Bounds to resize stack.
      * @param taskBounds Bounds to resize task.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizeDockedStack(Rect stackBounds, Rect taskBounds) {
+    public void resizePrimarySplitScreen(@NonNull Rect rootTaskBounds, @NonNull Rect taskBounds) {
         try {
-            getService().resizeDockedStack(stackBounds, taskBounds, null, null, null);
+            getService().resizePrimarySplitScreen(rootTaskBounds, taskBounds, null, null, null);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 3d966c7..7b25e25 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -16,13 +16,13 @@
 
 package android.app;
 
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
-import static android.content.pm.Checksum.WHOLE_MD5;
-import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA1;
-import static android.content.pm.Checksum.WHOLE_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA512;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
+import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
@@ -117,7 +117,6 @@
 
 import libcore.util.EmptyArray;
 
-import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
@@ -150,10 +149,11 @@
     private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
 
     /** Default set of checksums - includes all available checksums.
-     * @see PackageManager#getChecksums  */
+     * @see PackageManager#requestChecksums  */
     private static final int DEFAULT_CHECKSUMS =
-            WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
-                    | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
+            TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 | TYPE_WHOLE_MD5 | TYPE_WHOLE_SHA1 | TYPE_WHOLE_SHA256
+                    | TYPE_WHOLE_SHA512 | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256
+                    | TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
 
     // Name of the resource which provides background permission button string
     public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
@@ -994,14 +994,24 @@
     }
 
     @Override
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
             @NonNull IntentSender statusReceiver)
-            throws CertificateEncodingException, IOException, NameNotFoundException {
+            throws CertificateEncodingException, NameNotFoundException {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(trustedInstallers);
         try {
-            mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+            if (trustedInstallers == TRUST_ALL) {
+                trustedInstallers = null;
+            } else if (trustedInstallers == TRUST_NONE) {
+                trustedInstallers = Collections.emptyList();
+            } else if (trustedInstallers.isEmpty()) {
+                throw new IllegalArgumentException(
+                        "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+                                + "list of certificates.");
+            }
+            mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
                     encodeCertificates(trustedInstallers), statusReceiver, getUserId());
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1a4db4e..357b26c 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -25,7 +25,6 @@
 import android.app.IApplicationThread;
 import android.app.IActivityController;
 import android.app.IAppTask;
-import android.app.IAssistDataReceiver;
 import android.app.IInstrumentationWatcher;
 import android.app.IProcessObserver;
 import android.app.IServiceConnection;
@@ -70,7 +69,6 @@
 import android.os.StrictMode;
 import android.os.WorkSource;
 import android.service.voice.IVoiceInteractionSession;
-import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
 import com.android.internal.app.IVoiceInteractor;
@@ -449,9 +447,8 @@
     void hang(in IBinder who, boolean allowRestart);
 
     List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
-    @UnsupportedAppUsage
-    void moveTaskToStack(int taskId, int stackId, boolean toTop);
-    void setFocusedStack(int stackId);
+    void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop);
+    void setFocusedRootTask(int taskId);
     ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     @UnsupportedAppUsage
     void restart();
@@ -470,11 +467,6 @@
     @UnsupportedAppUsage
     boolean isInLockTaskMode();
     @UnsupportedAppUsage
-    void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
-            in IRecentsAnimationRunner recentsAnimationRunner);
-    @UnsupportedAppUsage
-    void cancelRecentsAnimation(boolean restoreHomeStackPosition);
-    @UnsupportedAppUsage
     int startActivityFromRecents(int taskId, in Bundle options);
     @UnsupportedAppUsage
     void startSystemLockTaskMode(int taskId);
@@ -514,15 +506,12 @@
     boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
     @UnsupportedAppUsage
     void suppressResizeConfigChanges(boolean suppress);
-    @UnsupportedAppUsage
-    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+    boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
     boolean isAppStartModeDisabled(int uid, in String packageName);
     @UnsupportedAppUsage
     boolean unlockUser(int userid, in byte[] token, in byte[] secret,
             in IProgressListener listener);
     void killPackageDependents(in String packageName, int userId);
-    @UnsupportedAppUsage
-    void removeStack(int stackId);
     void makePackageIdle(String packageName, int userId);
     int getMemoryTrimLevel();
     boolean isVrModePackageEnabled(in ComponentName packageName);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index e21ef8e..66007e5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -123,7 +123,7 @@
             in ProfilerInfo profilerInfo, in Bundle options, int userId);
     int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
             int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
-    void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
+    void startRecentsActivity(in Intent intent, in long eventTime,
             in IRecentsAnimationRunner recentsAnimationRunner);
     int startActivityFromRecents(int taskId, in Bundle options);
     int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
@@ -186,11 +186,11 @@
     void reportAssistContextExtras(in IBinder token, in Bundle extras,
             in AssistStructure structure, in AssistContent content, in Uri referrer);
 
-    void setFocusedStack(int stackId);
+    void setFocusedRootTask(int taskId);
     ActivityTaskManager.RootTaskInfo getFocusedRootTaskInfo();
     Rect getTaskBounds(int taskId);
 
-    void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+    void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
     void startLockTaskModeByToken(in IBinder token);
     void stopLockTaskModeByToken(in IBinder token);
     void updateLockTaskPackages(int userId, in String[] packages);
@@ -239,8 +239,7 @@
      * @return Return true on success. Otherwise false.
      */
     boolean resizeTask(int taskId, in Rect bounds, int resizeMode);
-    void moveStackToDisplay(int stackId, int displayId);
-    void removeStack(int stackId);
+    void moveRootTaskToDisplay(int taskId, int displayId);
 
     /**
      * Sets the windowing mode for a specific task. Only works on tasks of type
@@ -251,15 +250,15 @@
      * @return Whether the task was successfully put into the specified windowing mode.
      */
     boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
-    void moveTaskToStack(int taskId, int stackId, boolean toTop);
+    void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop);
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(in int[] windowingModes);
-    /** Removes stack of the activity types from the system. */
-    void removeStacksWithActivityTypes(in int[] activityTypes);
+    void removeRootTasksInWindowingModes(in int[] windowingModes);
+    /** Removes root tasks of the activity types from the system. */
+    void removeRootTasksWithActivityTypes(in int[] activityTypes);
 
     List<ActivityTaskManager.RootTaskInfo> getAllRootTaskInfos();
     ActivityTaskManager.RootTaskInfo getRootTaskInfo(int windowingMode, int activityType);
@@ -299,7 +298,7 @@
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
 
     void suppressResizeConfigChanges(boolean suppress);
-    boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
+    boolean moveTopActivityToPinnedRootTask(int rootTaskId, in Rect bounds);
     boolean enterPictureInPictureMode(in IBinder token, in PictureInPictureParams params);
     void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
     void requestPictureInPictureMode(in IBinder token);
@@ -325,7 +324,7 @@
      *                                 stacks.
      * @throws RemoteException
      */
-    void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
+    void resizePrimarySplitScreen(in Rect dockedBounds, in Rect tempDockedTaskBounds,
             in Rect tempDockedTaskInsetBounds,
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
 
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 19242ba..cb879fc 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -76,16 +76,24 @@
             OnCrossProfileWidgetProvidersChangeListener listener);
 
     /**
-     * Checks if an app with given uid is an active device admin of its user and has the policy
-     * specified.
+     * Checks if an app with given uid is an active device owner of its user.
      *
      * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held.
      *
      * @param uid App uid.
-     * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}.
-     * @return true if the uid is an active admin with the given policy.
+     * @return true if the uid is an active device owner.
      */
-    public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
+    public abstract boolean isActiveDeviceOwner(int uid);
+
+    /**
+     * Checks if an app with given uid is an active profile owner of its user.
+     *
+     * <p>This takes the DPMS lock.  DO NOT call from PM/UM/AM with their lock held.
+     *
+     * @param uid App uid.
+     * @return true if the uid is an active profile owner.
+     */
+    public abstract boolean isActiveProfileOwner(int uid);
 
     /**
      * Checks if an app with given uid is the active supervision admin.
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index 9864afb..4796d8d 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.timezonedetector.ITimeZoneDetectorService;
 import android.content.Context;
@@ -36,7 +37,7 @@
  *
  * @hide
  */
-// @SystemApi
+@SystemApi
 @SystemService(Context.TIME_MANAGER)
 public final class TimeManager {
     private static final String TAG = "time.TimeManager";
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index c62c2b3..df89c28 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TimeZoneDetector;
 import android.os.Parcel;
@@ -50,7 +51,7 @@
  *
  * @hide
  */
-// @SystemApi
+@SystemApi
 public final class TimeZoneCapabilities implements Parcelable {
 
     /** @hide */
diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
index 6a04f3f..b339e53 100644
--- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java
@@ -17,6 +17,7 @@
 package android.app.time;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,7 +28,7 @@
  *
  * @hide
  */
-// @SystemApi
+@SystemApi
 public final class TimeZoneCapabilitiesAndConfig implements Parcelable {
 
     public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR =
diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java
index 488818a..c0a0c21 100644
--- a/core/java/android/app/time/TimeZoneConfiguration.java
+++ b/core/java/android/app/time/TimeZoneConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -41,7 +42,7 @@
  *
  * @hide
  */
-// @SystemApi
+@SystemApi
 public final class TimeZoneConfiguration implements Parcelable {
 
     public static final @NonNull Creator<TimeZoneConfiguration> CREATOR =
@@ -188,7 +189,7 @@
      *
      * @hide
      */
-    // @SystemApi
+    @SystemApi
     public static final class Builder {
 
         private final Bundle mBundle = new Bundle();
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 6a68796..573892b 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2563,7 +2563,7 @@
      * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
      * <p>The link key is not required to be authenticated, i.e the communication may be
      * vulnerable to Person In the Middle attacks. For Bluetooth 2.1 devices,
-     * the link will be encrypted, as encryption is mandartory.
+     * the link will be encrypted, as encryption is mandatory.
      * For legacy devices (pre Bluetooth 2.1 devices) the link will not
      * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
      * encrypted and authenticated communication channel is desired.
@@ -2602,7 +2602,7 @@
      * an input and output capability or just has the ability to display a numeric key,
      * a secure socket connection is not possible and this socket can be used.
      * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
-     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory.
+     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandatory.
      * For more details, refer to the Security Model section 5.2 (vol 3) of
      * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index c2beab5..ea4b762 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1109,6 +1109,34 @@
     }
 
     /**
+     * Returns the reversed orientation.
+     * @hide
+     */
+    @ActivityInfo.ScreenOrientation
+    public static int reverseOrientation(@ActivityInfo.ScreenOrientation int orientation) {
+        switch (orientation) {
+            case SCREEN_ORIENTATION_LANDSCAPE:
+                return SCREEN_ORIENTATION_PORTRAIT;
+            case SCREEN_ORIENTATION_PORTRAIT:
+                return SCREEN_ORIENTATION_LANDSCAPE;
+            case SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+                return SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+            case SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+                return SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+            case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+                return SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+            case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+                return SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+            case SCREEN_ORIENTATION_USER_LANDSCAPE:
+                return SCREEN_ORIENTATION_USER_PORTRAIT;
+            case SCREEN_ORIENTATION_USER_PORTRAIT:
+                return SCREEN_ORIENTATION_USER_LANDSCAPE;
+            default:
+                return orientation;
+        }
+    }
+
+    /**
      * Returns true if the activity supports picture-in-picture.
      * @hide
      */
diff --git a/core/java/android/content/pm/ApkChecksum.java b/core/java/android/content/pm/ApkChecksum.java
index bf67841..eca48ec 100644
--- a/core/java/android/content/pm/ApkChecksum.java
+++ b/core/java/android/content/pm/ApkChecksum.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,12 +30,11 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.util.List;
 
 /**
  * A typed checksum of an APK.
  *
- * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
+ * @see PackageManager#requestChecksums
  */
 @DataClass(genHiddenConstructor = true)
 @DataClass.Suppress({"getChecksum"})
@@ -52,20 +50,20 @@
     /**
      * For Installer-provided checksums, package name of the Installer.
      */
-    private final @Nullable String mSourcePackageName;
+    private final @Nullable String mInstallerPackageName;
     /**
      * For Installer-provided checksums, certificate of the Installer.
      */
-    private final @Nullable byte[] mSourceCertificate;
+    private final @Nullable byte[] mInstallerCertificate;
 
     /**
      * Constructor, internal use only.
      *
      * @hide
      */
-    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind,
+    public ApkChecksum(@Nullable String splitName, @Checksum.Type int type,
             @NonNull byte[] value) {
-        this(splitName, new Checksum(kind, value), (String) null, (byte[]) null);
+        this(splitName, new Checksum(type, value), (String) null, (byte[]) null);
     }
 
     /**
@@ -73,19 +71,19 @@
      *
      * @hide
      */
-    public ApkChecksum(@Nullable String splitName, @Checksum.Kind int kind, @NonNull byte[] value,
+    public ApkChecksum(@Nullable String splitName, @Checksum.Type int type, @NonNull byte[] value,
             @Nullable String sourcePackageName, @Nullable Certificate sourceCertificate)
             throws CertificateEncodingException {
-        this(splitName, new Checksum(kind, value), sourcePackageName,
+        this(splitName, new Checksum(type, value), sourcePackageName,
                 (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
     }
 
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
-    public @Checksum.Kind int getKind() {
-        return mChecksum.getKind();
+    public @Checksum.Type int getType() {
+        return mChecksum.getType();
     }
 
     /**
@@ -96,24 +94,24 @@
     }
 
     /**
-     * Returns raw bytes representing encoded certificate of the source of this checksum.
+     * Returns raw bytes representing encoded certificate of the Installer.
      * @hide
      */
-    public @Nullable byte[] getSourceCertificateBytes() {
-        return mSourceCertificate;
+    public @Nullable byte[] getInstallerCertificateBytes() {
+        return mInstallerCertificate;
     }
 
     /**
-     * Certificate of the source of this checksum.
+     * For Installer-provided checksums, certificate of the Installer.
      * @throws CertificateException in case when certificate can't be re-created from serialized
      * data.
      */
-    public @Nullable Certificate getSourceCertificate() throws CertificateException {
-        if (mSourceCertificate == null) {
+    public @Nullable Certificate getInstallerCertificate() throws CertificateException {
+        if (mInstallerCertificate == null) {
             return null;
         }
         final CertificateFactory cf = CertificateFactory.getInstance("X.509");
-        final InputStream is = new ByteArrayInputStream(mSourceCertificate);
+        final InputStream is = new ByteArrayInputStream(mInstallerCertificate);
         final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
         return cert;
     }
@@ -140,9 +138,9 @@
      *   Checksum for which split. Null indicates base.apk.
      * @param checksum
      *   Checksum.
-     * @param sourcePackageName
+     * @param installerPackageName
      *   For Installer-provided checksums, package name of the Installer.
-     * @param sourceCertificate
+     * @param installerCertificate
      *   For Installer-provided checksums, certificate of the Installer.
      * @hide
      */
@@ -150,14 +148,14 @@
     public ApkChecksum(
             @Nullable String splitName,
             @NonNull Checksum checksum,
-            @Nullable String sourcePackageName,
-            @Nullable byte[] sourceCertificate) {
+            @Nullable String installerPackageName,
+            @Nullable byte[] installerCertificate) {
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
-        this.mSourcePackageName = sourcePackageName;
-        this.mSourceCertificate = sourceCertificate;
+        this.mInstallerPackageName = installerPackageName;
+        this.mInstallerCertificate = installerCertificate;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -174,8 +172,8 @@
      * For Installer-provided checksums, package name of the Installer.
      */
     @DataClass.Generated.Member
-    public @Nullable String getSourcePackageName() {
-        return mSourcePackageName;
+    public @Nullable String getInstallerPackageName() {
+        return mInstallerPackageName;
     }
 
     @Override
@@ -186,13 +184,13 @@
 
         byte flg = 0;
         if (mSplitName != null) flg |= 0x1;
-        if (mSourcePackageName != null) flg |= 0x4;
-        if (mSourceCertificate != null) flg |= 0x8;
+        if (mInstallerPackageName != null) flg |= 0x4;
+        if (mInstallerCertificate != null) flg |= 0x8;
         dest.writeByte(flg);
         if (mSplitName != null) dest.writeString(mSplitName);
         dest.writeTypedObject(mChecksum, flags);
-        if (mSourcePackageName != null) dest.writeString(mSourcePackageName);
-        if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
+        if (mInstallerPackageName != null) dest.writeString(mInstallerPackageName);
+        if (mInstallerCertificate != null) dest.writeByteArray(mInstallerCertificate);
     }
 
     @Override
@@ -209,15 +207,15 @@
         byte flg = in.readByte();
         String splitName = (flg & 0x1) == 0 ? null : in.readString();
         Checksum checksum = (Checksum) in.readTypedObject(Checksum.CREATOR);
-        String sourcePackageName = (flg & 0x4) == 0 ? null : in.readString();
-        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+        String installerPackageName = (flg & 0x4) == 0 ? null : in.readString();
+        byte[] installerCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
 
         this.mSplitName = splitName;
         this.mChecksum = checksum;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mChecksum);
-        this.mSourcePackageName = sourcePackageName;
-        this.mSourceCertificate = sourceCertificate;
+        this.mInstallerPackageName = installerPackageName;
+        this.mInstallerCertificate = installerCertificate;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -237,10 +235,10 @@
     };
 
     @DataClass.Generated(
-            time = 1600407436287L,
+            time = 1601589269293L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/ApkChecksum.java",
-            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mSourcePackageName\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.content.pm.Checksum.Kind int getKind()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getSourceCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.NonNull android.content.pm.Checksum mChecksum\nprivate final @android.annotation.Nullable java.lang.String mInstallerPackageName\nprivate final @android.annotation.Nullable byte[] mInstallerCertificate\npublic @android.content.pm.Checksum.Type int getType()\npublic @android.annotation.NonNull byte[] getValue()\npublic @android.annotation.Nullable byte[] getInstallerCertificateBytes()\npublic @android.annotation.Nullable java.security.cert.Certificate getInstallerCertificate()\nclass ApkChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/Checksum.java b/core/java/android/content/pm/Checksum.java
index 10ca5ca..318d363 100644
--- a/core/java/android/content/pm/Checksum.java
+++ b/core/java/android/content/pm/Checksum.java
@@ -25,12 +25,12 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.List;
 
 /**
  * A typed checksum.
  *
- * @see PackageInstaller.Session#addChecksums(String, List)
+ * @see ApkChecksum
+ * @see PackageManager#requestChecksums
  */
 @DataClass(genConstDefs = false)
 public final class Checksum implements Parcelable {
@@ -43,88 +43,89 @@
      * Can be used by kernel to enforce authenticity and integrity of the APK.
      * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst#">See fs-verity for details</a>
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+    public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
 
     /**
      * MD5 hash computed over all file bytes.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
-     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
-     *             MD5 is cryptographically broken and unsuitable for further use.
+     * @see PackageManager#requestChecksums
+     * @deprecated Not platform enforced. Cryptographically broken and unsuitable for further use.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
      *             Provided for completeness' sake and to support legacy usecases.
      */
     @Deprecated
-    public static final int WHOLE_MD5 = 0x00000002;
+    public static final int TYPE_WHOLE_MD5 = 0x00000002;
 
     /**
      * SHA1 hash computed over all file bytes.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
-     * @deprecated Use SHA2 family of hashes (SHA256/SHA512) instead.
-     *             SHA1 is broken and should not be used.
+     * @see PackageManager#requestChecksums
+     * @deprecated Not platform enforced. Broken and should not be used.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
      *             Provided for completeness' sake and to support legacy usecases.
      */
     @Deprecated
-    public static final int WHOLE_SHA1 = 0x00000004;
+    public static final int TYPE_WHOLE_SHA1 = 0x00000004;
 
     /**
      * SHA256 hash computed over all file bytes.
+     * @deprecated Not platform enforced.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
+     *             Provided for completeness' sake and to support legacy usecases.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_SHA256 = 0x00000008;
+    @Deprecated
+    public static final int TYPE_WHOLE_SHA256 = 0x00000008;
 
     /**
      * SHA512 hash computed over all file bytes.
+     * @deprecated Not platform enforced.
+     *             Use platform enforced digests e.g. {@link #TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}.
+     *             Provided for completeness' sake and to support legacy usecases.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int WHOLE_SHA512 = 0x00000010;
+    @Deprecated
+    public static final int TYPE_WHOLE_SHA512 = 0x00000010;
 
     /**
      * Root SHA256 hash of a 1M Merkle tree computed over protected content.
      * Excludes signing block.
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+    public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
 
     /**
      * Root SHA512 hash of a 1M Merkle tree computed over protected content.
      * Excludes signing block.
      * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
      *
-     * @see PackageManager#getChecksums
-     * @see PackageInstaller.Session#addChecksums
+     * @see PackageManager#requestChecksums
      */
-    public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+    public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
-            WHOLE_MERKLE_ROOT_4K_SHA256,
-            WHOLE_MD5,
-            WHOLE_SHA1,
-            WHOLE_SHA256,
-            WHOLE_SHA512,
-            PARTIAL_MERKLE_ROOT_1M_SHA256,
-            PARTIAL_MERKLE_ROOT_1M_SHA512,
+    @IntDef(flag = true, prefix = {"TYPE_"}, value = {
+            TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+            TYPE_WHOLE_MD5,
+            TYPE_WHOLE_SHA1,
+            TYPE_WHOLE_SHA256,
+            TYPE_WHOLE_SHA512,
+            TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+            TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Kind {}
+    public @interface Type {}
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
-    private final @Checksum.Kind int mKind;
+    private final @Checksum.Type int mType;
     /**
      * Checksum value.
      */
@@ -132,7 +133,6 @@
 
 
 
-
     // Code below generated by codegen v1.0.15.
     //
     // DO NOT MODIFY!
@@ -149,18 +149,18 @@
     /**
      * Creates a new Checksum.
      *
-     * @param kind
-     *   Checksum kind.
+     * @param type
+     *   Checksum type.
      * @param value
      *   Checksum value.
      */
     @DataClass.Generated.Member
     public Checksum(
-            @Checksum.Kind int kind,
+            @Checksum.Type int type,
             @NonNull byte[] value) {
-        this.mKind = kind;
+        this.mType = type;
         com.android.internal.util.AnnotationValidations.validate(
-                Checksum.Kind.class, null, mKind);
+                Checksum.Type.class, null, mType);
         this.mValue = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mValue);
@@ -169,11 +169,11 @@
     }
 
     /**
-     * Checksum kind.
+     * Checksum type.
      */
     @DataClass.Generated.Member
-    public @Checksum.Kind int getKind() {
-        return mKind;
+    public @Checksum.Type int getType() {
+        return mType;
     }
 
     /**
@@ -190,7 +190,7 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        dest.writeInt(mKind);
+        dest.writeInt(mType);
         dest.writeByteArray(mValue);
     }
 
@@ -205,12 +205,12 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        int kind = in.readInt();
+        int type = in.readInt();
         byte[] value = in.createByteArray();
 
-        this.mKind = kind;
+        this.mType = type;
         com.android.internal.util.AnnotationValidations.validate(
-                Checksum.Kind.class, null, mKind);
+                Checksum.Type.class, null, mType);
         this.mValue = value;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mValue);
@@ -233,10 +233,10 @@
     };
 
     @DataClass.Generated(
-            time = 1600717052366L,
+            time = 1601955017774L,
             codegenVersion = "1.0.15",
             sourceFile = "frameworks/base/core/java/android/content/pm/Checksum.java",
-            inputSignatures = "public static final  int WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int WHOLE_MD5\npublic static final @java.lang.Deprecated int WHOLE_SHA1\npublic static final  int WHOLE_SHA256\npublic static final  int WHOLE_SHA512\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Kind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
+            inputSignatures = "public static final  int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_MD5\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA1\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA256\npublic static final @java.lang.Deprecated int TYPE_WHOLE_SHA512\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256\npublic static final  int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512\nprivate final @android.content.pm.Checksum.Type int mType\nprivate final @android.annotation.NonNull byte[] mValue\nclass Checksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstDefs=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ba894ae..79e3eea 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -749,7 +749,7 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
-    void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
 
     //------------------------------------------------------------------------
     //
diff --git a/core/java/android/content/pm/IncrementalStatesInfo.aidl b/core/java/android/content/pm/IncrementalStatesInfo.aidl
index b5aad12..7064fbf 100644
--- a/core/java/android/content/pm/IncrementalStatesInfo.aidl
+++ b/core/java/android/content/pm/IncrementalStatesInfo.aidl
@@ -15,6 +15,6 @@
 ** limitations under the License.
 */
 
-package com.android.server.pm;
+package android.content.pm;
 
-parcelable IncrementalStatesInfo;
\ No newline at end of file
+parcelable IncrementalStatesInfo;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index a9f1439..f03425b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1222,13 +1222,15 @@
          * Adds installer-provided checksums for the APK file in session.
          *
          * @param name      previously written as part of this session.
+         *                  {@link #openWrite}
          * @param checksums installer intends to make available via
-         *                  {@link PackageManager#getChecksums(String, boolean, int, List,
-         *                  IntentSender)}.
+         *                  {@link PackageManager#requestChecksums}.
          * @throws SecurityException if called after the session has been
          *                           committed or abandoned.
-         * @deprecated  use platform-enforced checksums e.g.
-         *              {@link Checksum#WHOLE_MERKLE_ROOT_4K_SHA256}
+         * @deprecated  do not use installer-provided checksums,
+         *              use platform-enforced checksums
+         *              e.g. {@link Checksum#TYPE_WHOLE_MERKLE_ROOT_4K_SHA256}
+         *              in {@link PackageManager#requestChecksums}.
          */
         @Deprecated
         public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c293e4ad..1a992f5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -79,7 +79,6 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.cert.Certificate;
@@ -3309,7 +3308,7 @@
     /**
      * Extra field name for the ID of a package pending verification. Passed to
      * a package verifier and is used to call back to
-     * @see #getChecksums
+     * @see #requestChecksums
      */
     public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
 
@@ -7987,19 +7986,20 @@
 
     /**
      * Trust any Installer to provide checksums for the package.
-     * @see #getChecksums
+     * @see #requestChecksums
      */
-    public static final @Nullable List<Certificate> TRUST_ALL = null;
+    public static final @Nullable List<Certificate> TRUST_ALL = Collections.singletonList(null);
 
     /**
      * Don't trust any Installer to provide checksums for the package.
      * This effectively disables optimized Installer-enforced checksums.
-     * @see #getChecksums
+     * @see #requestChecksums
      */
-    public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList();
+    public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
 
     /**
-     * Returns the checksums for APKs within a package.
+     * Requesting the checksums for APKs within a package.
+     * The checksums will be returned asynchronously via statusReceiver.
      *
      * By default returns all readily available checksums:
      * - enforced by platform,
@@ -8011,21 +8011,24 @@
      *
      * @param packageName whose checksums to return.
      * @param includeSplits whether to include checksums for non-base splits.
-     * @param required explicitly request the checksum kinds. Will incur significant
+     * @param required explicitly request the checksum types. May incur significant
      *                 CPU/memory/disk usage.
-     * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted.
-     *                          {@link #TRUST_ALL} will return checksums from any Installer,
-     *                          {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
+     * @param trustedInstallers for checksums enforced by installer, which installers are to be
+     *                          trusted.
+     *                          {@link #TRUST_ALL} will return checksums from any installer,
+     *                          {@link #TRUST_NONE} disables optimized installer-enforced checksums,
+     *                          otherwise the list has to be non-empty list of certificates.
      * @param statusReceiver called once when the results are available as
-     *                       {@link #EXTRA_CHECKSUMS} of type ApkChecksum[].
+     *                       {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
      * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+     * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
      * @throws NameNotFoundException if a package with the given name cannot be found on the system.
      */
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int required, @Nullable List<Certificate> trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
             @NonNull IntentSender statusReceiver)
-            throws CertificateEncodingException, IOException, NameNotFoundException {
-        throw new UnsupportedOperationException("getChecksums not implemented in subclass");
+            throws CertificateEncodingException, NameNotFoundException {
+        throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 9197020..b936c63 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1080,14 +1080,57 @@
                 }
             }
 
-            final String requiredFeature = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestUsesPermission_requiredFeature, 0);
-
-            final String requiredNotfeature = sa.getNonConfigurationString(
-                    R.styleable.AndroidManifestUsesPermission_requiredNotFeature,
+            final ArraySet<String> requiredFeatures = new ArraySet<>();
+            String feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable.AndroidManifestUsesPermission_requiredFeature,
                     0);
+            if (feature != null) {
+                requiredFeatures.add(feature);
+            }
 
-            XmlUtils.skipCurrentTag(parser);
+            final ArraySet<String> requiredNotFeatures = new ArraySet<>();
+            feature = sa.getNonConfigurationString(
+                    com.android.internal.R.styleable
+                            .AndroidManifestUsesPermission_requiredNotFeature,
+                    0);
+            if (feature != null) {
+                requiredNotFeatures.add(feature);
+            }
+
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG
+                    || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                final ParseResult<?> result;
+                switch (parser.getName()) {
+                    case "required-feature":
+                        result = parseRequiredFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    case "required-not-feature":
+                        result = parseRequiredNotFeature(input, res, parser);
+                        if (result.isSuccess()) {
+                            requiredNotFeatures.add((String) result.getResult());
+                        }
+                        break;
+
+                    default:
+                        result = ParsingUtils.unknownTag("<uses-permission>", pkg, parser, input);
+                        break;
+                }
+
+                if (result.isError()) {
+                    return input.error(result);
+                }
+            }
 
             // Can only succeed from here on out
             ParseResult<ParsingPackage> success = input.success(pkg);
@@ -1100,17 +1143,22 @@
                 return success;
             }
 
-            // Only allow requesting this permission if the platform supports the given feature.
-            if (requiredFeature != null && mCallback != null && !mCallback.hasFeature(
-                    requiredFeature)) {
-                return success;
-            }
+            if (mCallback != null) {
+                // Only allow requesting this permission if the platform supports all of the
+                // "required-feature"s.
+                for (int i = requiredFeatures.size() - 1; i >= 0; i--) {
+                    if (!mCallback.hasFeature(requiredFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
 
-            // Only allow requesting this permission if the platform doesn't support the given
-            // feature.
-            if (requiredNotfeature != null && mCallback != null
-                    && mCallback.hasFeature(requiredNotfeature)) {
-                return success;
+                // Only allow requesting this permission if the platform does not supports any of
+                // the "required-not-feature"s.
+                for (int i = requiredNotFeatures.size() - 1; i >= 0; i--) {
+                    if (mCallback.hasFeature(requiredNotFeatures.valueAt(i))) {
+                        return success;
+                    }
+                }
             }
 
             if (!pkg.getRequestedPermissions().contains(name)) {
@@ -1127,6 +1175,36 @@
         }
     }
 
+    private ParseResult<String> parseRequiredFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
+    private ParseResult<String> parseRequiredNotFeature(ParseInput input, Resources res,
+            AttributeSet attrs) {
+        final TypedArray sa = res.obtainAttributes(attrs,
+                com.android.internal.R.styleable.AndroidManifestRequiredNotFeature);
+        try {
+            final String featureName = sa.getString(
+                    R.styleable.AndroidManifestRequiredNotFeature_name);
+            return TextUtils.isEmpty(featureName)
+                    ? input.error("Feature name is missing from <required-not-feature> tag.")
+                    : input.success(featureName);
+        } finally {
+            sa.recycle();
+        }
+    }
+
     private static ParseResult<ParsingPackage> parseUsesConfiguration(ParseInput input,
             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
         ConfigurationInfo cPref = new ConfigurationInfo();
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index dd820fa..4f46160 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,17 +16,22 @@
 
 package android.hardware.input;
 
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.os.Binder;
+import android.os.BlockUntrustedTouchesMode;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -48,8 +53,10 @@
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.VerifiedInputEvent;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -69,6 +76,13 @@
     private static final int MSG_DEVICE_REMOVED = 2;
     private static final int MSG_DEVICE_CHANGED = 3;
 
+    /** @hide */
+    public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = {
+            BlockUntrustedTouchesMode.DISABLED,
+            BlockUntrustedTouchesMode.PERMISSIVE,
+            BlockUntrustedTouchesMode.BLOCK
+    };
+
     private static InputManager sInstance;
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -168,6 +182,32 @@
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
+     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
+     * @hide
+     */
+    public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;
+
+    /**
+     * Default mode of the block untrusted touches mode feature.
+     * @hide
+     */
+    @BlockUntrustedTouchesMode
+    public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE =
+            BlockUntrustedTouchesMode.DISABLED;
+
+    /**
+     * Prevent touches from being consumed by apps if these touches passed through a non-trusted
+     * window from a different UID and are considered unsafe.
+     *
+     * TODO(b/158002302): Turn the feature on by default
+     *
+     * @hide
+     */
+    @TestApi
+    @ChangeId
+    public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;
+
+    /**
      * Input Event Injection Synchronization Mode: None.
      * Never blocks.  Injection is asynchronous and is assumed always to be successful.
      * @hide
@@ -832,6 +872,103 @@
     }
 
     /**
+     * Returns the maximum allowed obscuring opacity by UID to propagate touches.
+     *
+     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+     * above the touch-consuming window.
+     *
+     * @see #setMaximumObscuringOpacityForTouch(Context, float)
+     *
+     * @hide
+     */
+    @TestApi
+    public float getMaximumObscuringOpacityForTouch(@NonNull Context context) {
+        return Settings.Global.getFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
+                DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
+    }
+
+    /**
+     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
+     *
+     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+     * above the touch-consuming window.
+     *
+     * For a certain UID:
+     * <ul>
+     *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+     *     the touch.
+     *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
+     *     window, compute their combined obscuring opacity considering that {@code
+     *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+     *     lesser than or equal to this setting and there are no other windows preventing the
+     *     touch, allow the UID to propagate the touch.
+     * </ul>
+     *
+     * This value should be between 0 (inclusive) and 1 (inclusive).
+     *
+     * @see #getMaximumObscuringOpacityForTouch(Context)
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) {
+        if (opacity < 0 || opacity > 1) {
+            throw new IllegalArgumentException(
+                    "Maximum obscuring opacity for touch should be >= 0 and <= 1");
+        }
+        Settings.Global.putFloat(context.getContentResolver(),
+                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
+    }
+
+    /**
+     * Returns the current mode of the block untrusted touches feature, one of:
+     * <ul>
+     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
+     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
+     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
+     * </ul>
+     *
+     * @hide
+     */
+    @TestApi
+    @BlockUntrustedTouchesMode
+    public int getBlockUntrustedTouchesMode(@NonNull Context context) {
+        int mode = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
+        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
+            Log.w(TAG, "Unknown block untrusted touches feature mode " + mode + ", using "
+                    + "default " + DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
+            return DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE;
+        }
+        return mode;
+    }
+
+    /**
+     * Sets the mode of the block untrusted touches feature to one of:
+     * <ul>
+     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
+     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
+     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
+     * </ul>
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void setBlockUntrustedTouchesMode(@NonNull Context context,
+            @BlockUntrustedTouchesMode int mode) {
+        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
+            throw new IllegalArgumentException("Invalid feature mode " + mode);
+        }
+        Settings.Global.putInt(context.getContentResolver(),
+                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, mode);
+    }
+
+    /**
      * Queries the framework about whether any physical keys exist on the
      * any keyboard attached to the device that are capable of producing the given
      * array of key codes.
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 9e332e9..06203ff 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -657,16 +657,8 @@
         argsForZygote.add("--runtime-flags=" + runtimeFlags);
         if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
             argsForZygote.add("--mount-external-default");
-        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
-            argsForZygote.add("--mount-external-read");
-        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
-            argsForZygote.add("--mount-external-write");
-        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
-            argsForZygote.add("--mount-external-full");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
             argsForZygote.add("--mount-external-installer");
-        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_LEGACY) {
-            argsForZygote.add("--mount-external-legacy");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
             argsForZygote.add("--mount-external-pass-through");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE) {
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index a79a1cf..4379ce1 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -70,13 +70,6 @@
     public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy);
 
     /**
-     * Notify the mount service that the mount policy for a UID changed.
-     * @param uid The UID for which policy changed.
-     * @param packageName The package in the UID for making the call.
-     */
-    public abstract void onExternalStoragePolicyChanged(int uid, String packageName);
-
-    /**
      * Gets the mount mode to use for a given UID as determined by consultin all
      * policies.
      *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1dbf95f..4ff0638 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -81,6 +81,7 @@
 import android.util.Log;
 import android.util.MemoryIntArray;
 import android.view.Display;
+import android.view.WindowManager.LayoutParams;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -3567,6 +3568,9 @@
             if (outConfig.fontScale < 0) {
                 outConfig.fontScale = DEFAULT_FONT_SCALE;
             }
+            outConfig.forceBoldText = Settings.Secure.getIntForUser(
+                    cr, Settings.Secure.FORCE_BOLD_TEXT, Configuration.FORCE_BOLD_TEXT_NO,
+                    userHandle);
 
             final String localeValue =
                     Settings.System.getStringForUser(cr, SYSTEM_LOCALES, userHandle);
@@ -3597,6 +3601,7 @@
             if (!inoutConfig.userSetLocale && !inoutConfig.getLocales().isEmpty()) {
                 inoutConfig.clearLocales();
             }
+            inoutConfig.forceBoldText = Configuration.FORCE_BOLD_TEXT_UNDEFINED;
         }
 
         /**
@@ -3620,7 +3625,11 @@
                             DEFAULT_OVERRIDEABLE_BY_RESTORE);
         }
 
-        /** @hide */
+        /**
+         * Convenience function for checking if settings should be overwritten with config changes.
+         * @see #putConfigurationForUser(ContentResolver, Configuration, int)
+         * @hide
+         */
         public static boolean hasInterestingConfigurationChanges(int changes) {
             return (changes & ActivityInfo.CONFIG_FONT_SCALE) != 0 ||
                     (changes & ActivityInfo.CONFIG_LOCALE) != 0;
@@ -13407,15 +13416,6 @@
                 "power_button_very_long_press";
 
         /**
-         * Global settings that shouldn't be persisted.
-         *
-         * @hide
-         */
-        public static final String[] TRANSIENT_SETTINGS = {
-                LOCATION_GLOBAL_KILL_SWITCH,
-        };
-
-        /**
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
          *
@@ -14486,6 +14486,48 @@
          * @hide
          */
         public static final String SHOW_PEOPLE_SPACE = "show_people_space";
+
+        /**
+         * Block untrusted touches mode.
+         *
+         * Can be one of:
+         * <ul>
+         *      <li>0 = {@link BlockUntrustedTouchesMode#DISABLED}: Feature is off.
+         *      <li>1 = {@link BlockUntrustedTouchesMode#PERMISSIVE}: Untrusted touches are flagged
+         *          but not blocked
+         *      <li>2 = {@link BlockUntrustedTouchesMode#BLOCK}: Untrusted touches are blocked
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
+
+        /**
+         * The maximum allowed obscuring opacity by UID to propagate touches.
+         *
+         * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
+         * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
+         * above the touch-consuming window.
+         *
+         * For a certain UID:
+         * <ul>
+         *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
+         *     the touch.
+         *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
+         *     window, compute their combined obscuring opacity considering that {@code
+         *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
+         *     lesser than or equal to this setting and there are no other windows preventing the
+         *     touch, allow the UID to propagate the touch.
+         * </ul>
+         *
+         * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context)
+         * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context,
+         * float)
+         *
+         * @hide
+         */
+        public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
+                "maximum_obscuring_opacity_for_touch";
     }
 
     /**
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 70224f9..e520d7c 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -673,8 +673,6 @@
         ThreadedRenderer.setFPSDivisor(divisor);
     }
 
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
-            + "#postFrameCallback} instead")
     void doFrame(long frameTimeNanos, int frame, long frameTimelineVsyncId) {
         final long startNanos;
         synchronized (mLock) {
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 430e2cf..51474d3 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.util.Log;
@@ -158,8 +157,6 @@
      * @param frameTimelineVsyncId The frame timeline vsync id, used to correlate a frame
      * produced by HWUI with the timeline data stored in Surface Flinger.
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
-            + "Choreographer#postFrameCallback} instead")
     public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
             long frameTimelineVsyncId) {
     }
@@ -203,8 +200,6 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link "
-            + "Choreographer#postFrameCallback} instead")
     private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
             long frameTimelineVsyncId) {
         onVsync(timestampNanos, physicalDisplayId, frame, frameTimelineVsyncId);
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 1ef701f..d1a9a05 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.graphics.Region;
 import android.os.IBinder;
+import android.os.TouchOcclusionMode;
 
 import java.lang.ref.WeakReference;
 
@@ -82,10 +83,18 @@
     // Window is trusted overlay.
     public boolean trustedOverlay;
 
+    // What effect this window has on touch occlusion if it lets touches pass through
+    // By default windows will block touches if they are untrusted and from a different UID due to
+    // security concerns
+    public int touchOcclusionMode = TouchOcclusionMode.BLOCK_UNTRUSTED;
+
     // Id of process and user that owns the window.
     public int ownerPid;
     public int ownerUid;
 
+    // Owner package of the window
+    public String packageName;
+
     // Window input features.
     public int inputFeatures;
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 44e603e..0c64eea 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -182,9 +182,6 @@
             IBinder displayToken, int mode);
     private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
             long barrierObject, long frame);
-    private static native void nativeDeferTransactionUntilSurface(long transactionObj,
-            long nativeObject,
-            long surfaceObject, long frame);
     private static native void nativeReparentChildren(long transactionObj, long nativeObject,
             long newParentObject);
     private static native void nativeReparent(long transactionObj, long nativeObject,
@@ -2947,22 +2944,6 @@
         /**
          * @hide
          */
-        @Deprecated
-        @UnsupportedAppUsage
-        public Transaction deferTransactionUntilSurface(SurfaceControl sc, Surface barrierSurface,
-                long frameNumber) {
-            if (frameNumber < 0) {
-                return this;
-            }
-            checkPreconditions(sc);
-            nativeDeferTransactionUntilSurface(mNativeObject, sc.mNativeObject,
-                    barrierSurface.mNativeObject, frameNumber);
-            return this;
-        }
-
-        /**
-         * @hide
-         */
         public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
             checkPreconditions(sc);
             nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b653d21..e00ff7e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -78,6 +78,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_FOCUS_CONTROLLER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INSETS_CONTROLLER;
 
@@ -2707,7 +2708,13 @@
                 updateColorModeIfNeeded(lp.getColorMode());
                 surfaceCreated = !hadSurface && mSurface.isValid();
                 surfaceDestroyed = hadSurface && !mSurface.isValid();
-                surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId())
+                // When using Blast, the surface generation id may not change when there's a new
+                // SurfaceControl. In that case, we also check relayout flag
+                // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new
+                // SurfaceControl.
+                surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()
+                        || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
+                        == RELAYOUT_RES_SURFACE_CHANGED)
                         && mSurface.isValid();
 
                 if (cutoutChanged) {
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index 5bcfa8b..2e5ee04 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.S;
+    public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.R;
 
     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 5fc9344..8790bbd 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.WebViewChromiumFactoryProviderForS";
+            "com.android.webview.chromium.WebViewChromiumFactoryProviderForR";
 
     private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
 
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index d8f2bb2..a7cb642 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -39,13 +39,14 @@
     private ITaskOrganizerController mTaskOrganizerController;
 
     public TaskOrganizer() {
-        mTaskOrganizerController = getController();
+        this(null);
     }
 
     /** @hide */
     @VisibleForTesting
     public TaskOrganizer(ITaskOrganizerController taskOrganizerController) {
-        mTaskOrganizerController = taskOrganizerController;
+        mTaskOrganizerController = taskOrganizerController != null
+                ? taskOrganizerController : getController();
     }
 
     /**
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 900e18d..28a9601 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -166,7 +166,7 @@
 
         // Now fetch app icon and raster with no badging even in work profile
         Bitmap appIcon = mSelectableTargetInfoCommunicator.makePresentationGetter(info)
-                .getIconBitmap(UserHandle.getUserHandleForUid(UserHandle.myUserId()));
+                .getIconBitmap(android.os.Process.myUserHandle());
 
         // Raster target drawable with appIcon as a badge
         SimpleIconFactory sif = SimpleIconFactory.obtain(mContext);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e60f7fc..dd4409c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -44,26 +44,37 @@
     private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
-    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 1;
-    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0;
-    public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0;
-    public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 0;
-    public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 0;
-    public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 0;
-    public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 0;
-    public static final int CUJ_LAUNCHER_QUICK_SWITCH = 0;
+    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
+    public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
+    public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
+    public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
+    public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
+    public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 5;
+    public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 6;
+    public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS = 7;
+    public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON = 8;
+    public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME = 9;
+    public static final int CUJ_LAUNCHER_APP_CLOSE_TO_PIP = 10;
+    public static final int CUJ_LAUNCHER_QUICK_SWITCH = 11;
 
     private static final int NO_STATSD_LOGGING = -1;
 
     // Used to convert CujType to InteractionType enum value for statsd logging.
     // Use NO_STATSD_LOGGING in case the measurement for a given CUJ should not be logged to statsd.
-    private static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
-            NO_STATSD_LOGGING,
+    @VisibleForTesting
+    public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
             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,
     };
 
     private static volatile InteractionJankMonitor sInstance;
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 2ba372a..0843741 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -18,10 +18,10 @@
 
 import android.annotation.Nullable;
 import android.os.Process;
+import android.util.IntArray;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
 import java.io.IOException;
@@ -456,14 +456,14 @@
          * cluster has started.
          */
         private static int[] getClusterStartIndices(long[] frequencies) {
-            ArrayList<Integer> indices = new ArrayList<>();
+            IntArray indices = new IntArray();
             indices.add(0);
             for (int i = 0; i < frequencies.length - 1; i++) {
                 if (frequencies[i] >= frequencies[i + 1]) {
                     indices.add(i + 1);
                 }
             }
-            return ArrayUtils.convertToIntArray(indices);
+            return indices.toArray();
         }
 
         /** Get the index in frequencies where each bucket starts */
@@ -477,7 +477,7 @@
                 return Arrays.copyOfRange(clusterStartIndices, 0, targetNumBuckets);
             }
 
-            ArrayList<Integer> bucketStartIndices = new ArrayList<>();
+            IntArray bucketStartIndices = new IntArray();
             for (int clusterIdx = 0; clusterIdx < numClusters; clusterIdx++) {
                 final int clusterStartIdx = getLowerBound(clusterIdx, clusterStartIndices);
                 final int clusterEndIdx =
@@ -509,7 +509,7 @@
                     bucketStartIndices.add(bucketStartIdx);
                 }
             }
-            return ArrayUtils.convertToIntArray(bucketStartIndices);
+            return bucketStartIndices.toArray();
         }
 
         private static int getLowerBound(int index, int[] startIndices) {
diff --git a/core/java/com/android/internal/os/ProcTimeInStateReader.java b/core/java/com/android/internal/os/ProcTimeInStateReader.java
index 2318473..d54a9c7 100644
--- a/core/java/com/android/internal/os/ProcTimeInStateReader.java
+++ b/core/java/com/android/internal/os/ProcTimeInStateReader.java
@@ -18,16 +18,11 @@
 
 import android.annotation.Nullable;
 import android.os.Process;
-
-import com.android.internal.util.ArrayUtils;
+import android.util.IntArray;
 
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
 
 /**
  * Reads and parses {@code time_in_state} files in the {@code proc} filesystem.
@@ -68,24 +63,25 @@
      * The format of a single line of the {@code time_in_state} file that exports the frequency
      * values
      */
-    private static final List<Integer> TIME_IN_STATE_LINE_FREQUENCY_FORMAT = Arrays.asList(
+    private static final int[] TIME_IN_STATE_LINE_FREQUENCY_FORMAT = new int[] {
             Process.PROC_OUT_LONG | Process.PROC_SPACE_TERM,
             Process.PROC_NEWLINE_TERM
-    );
+    };
 
     /**
      * The format of a single line of the {@code time_in_state} file that exports the time values
      */
-    private static final List<Integer> TIME_IN_STATE_LINE_TIME_FORMAT = Arrays.asList(
+    private static final int[] TIME_IN_STATE_LINE_TIME_FORMAT = new int[] {
             Process.PROC_SPACE_TERM,
             Process.PROC_OUT_LONG | Process.PROC_NEWLINE_TERM
-    );
+    };
 
     /**
      * The format of a header line of the {@code time_in_state} file
      */
-    private static final List<Integer> TIME_IN_STATE_HEADER_LINE_FORMAT =
-            Collections.singletonList(Process.PROC_NEWLINE_TERM);
+    private static final int[] TIME_IN_STATE_HEADER_LINE_FORMAT = new int[] {
+            Process.PROC_NEWLINE_TERM
+    };
 
     /**
      * The format of the {@code time_in_state} file to extract times, defined using {@link
@@ -166,8 +162,8 @@
         // formats. These formats are used to extract either the frequencies or the times from a
         // time_in_state file
         // Also check if each line is a header, and handle this in the created format arrays
-        ArrayList<Integer> timeInStateFrequencyFormat = new ArrayList<>();
-        ArrayList<Integer> timeInStateTimeFormat = new ArrayList<>();
+        IntArray timeInStateFrequencyFormat = new IntArray();
+        IntArray timeInStateTimeFormat = new IntArray();
         int numFrequencies = 0;
         for (int i = 0; i < timeInStateBytes.length; i++) {
             // If the first character of the line is not a digit, we treat it as a header
@@ -194,12 +190,12 @@
         final long[] readLongs = new long[numFrequencies];
         final boolean readSuccess = Process.parseProcLine(
                 timeInStateBytes, 0, timeInStateBytes.length,
-                ArrayUtils.convertToIntArray(timeInStateFrequencyFormat), null, readLongs, null);
+                timeInStateFrequencyFormat.toArray(), null, readLongs, null);
         if (!readSuccess) {
             throw new IOException("Failed to parse time_in_state file");
         }
 
-        mTimeInStateTimeFormat = ArrayUtils.convertToIntArray(timeInStateTimeFormat);
+        mTimeInStateTimeFormat = timeInStateTimeFormat.toArray();
         mFrequenciesKhz = readLongs;
     }
 }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index a985965..5e20cd0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -172,23 +172,11 @@
     public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
     /** Default external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_DEFAULT = IVold.REMOUNT_MODE_DEFAULT;
-    /** Read-only external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
-    /** Read-write external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
-    /**
-     * Mount mode for apps that are already installed on the device before the isolated_storage
-     * feature is enabled.
-     */
-    public static final int MOUNT_EXTERNAL_LEGACY = IVold.REMOUNT_MODE_LEGACY;
     /**
      * Mount mode for package installers which should give them access to
      * all obb dirs in addition to their package sandboxes
      */
     public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
-    /** Read-write external storage should be mounted instead of package sandbox */
-    public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
-
     /** The lower file system should be bind mounted directly on external storage */
     public static final int MOUNT_EXTERNAL_PASS_THROUGH = IVold.REMOUNT_MODE_PASS_THROUGH;
 
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index ed07432..32b808a 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -380,16 +380,8 @@
                 mNiceName = getAssignmentValue(arg);
             } else if (arg.equals("--mount-external-default")) {
                 mMountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
-            } else if (arg.equals("--mount-external-read")) {
-                mMountExternal = Zygote.MOUNT_EXTERNAL_READ;
-            } else if (arg.equals("--mount-external-write")) {
-                mMountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
-            } else if (arg.equals("--mount-external-full")) {
-                mMountExternal = Zygote.MOUNT_EXTERNAL_FULL;
-            }  else if (arg.equals("--mount-external-installer")) {
+            } else if (arg.equals("--mount-external-installer")) {
                 mMountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
-            }  else if (arg.equals("--mount-external-legacy")) {
-                mMountExternal = Zygote.MOUNT_EXTERNAL_LEGACY;
             } else if (arg.equals("--mount-external-pass-through")) {
                 mMountExternal = Zygote.MOUNT_EXTERNAL_PASS_THROUGH;
             } else if (arg.equals("--mount-external-android-writable")) {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f317f3f..c6dea18 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -310,6 +310,10 @@
         return total;
     }
 
+    /**
+     * @deprecated use {@code IntArray} instead
+     */
+    @Deprecated
     public static int[] convertToIntArray(List<Integer> list) {
         int[] array = new int[list.size()];
         for (int i = 0; i < list.size(); i++) {
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index a063820..463d909 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -60,8 +60,10 @@
     jfieldID hasWallpaper;
     jfieldID paused;
     jfieldID trustedOverlay;
+    jfieldID touchOcclusionMode;
     jfieldID ownerPid;
     jfieldID ownerUid;
+    jfieldID packageName;
     jfieldID inputFeatures;
     jfieldID displayId;
     jfieldID portalToDisplayId;
@@ -150,10 +152,13 @@
     mInfo.paused = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.paused);
     mInfo.trustedOverlay = env->GetBooleanField(obj, gInputWindowHandleClassInfo.trustedOverlay);
+    mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
+            env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
     mInfo.ownerPid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerPid);
     mInfo.ownerUid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerUid);
+    mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
     mInfo.inputFeatures = static_cast<InputWindowInfo::Feature>(
             env->GetIntField(obj, gInputWindowHandleClassInfo.inputFeatures));
     mInfo.displayId = env->GetIntField(obj,
@@ -326,12 +331,17 @@
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.trustedOverlay, clazz, "trustedOverlay", "Z");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.touchOcclusionMode, clazz, "touchOcclusionMode", "I");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz,
             "ownerPid", "I");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz,
             "ownerUid", "I");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.packageName, clazz, "packageName",
+                 "Ljava/lang/String;");
+
     GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz,
             "inputFeatures", "I");
 
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 0892b70..355ef0c 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -612,50 +612,77 @@
     return ret;
 }
 
+// String tries to allocate itself on the stack, within a known size, but will
+// make a heap allocation if not.
+template <size_t StackReserve>
+class StackString {
+public:
+    StackString(JNIEnv* env, jstring str) : mEnv(env), mJStr(str) {
+        LOG_ALWAYS_FATAL_IF(str == nullptr);
+        mSize = env->GetStringLength(str);
+        if (mSize > StackReserve) {
+            mStr = new jchar[mSize];
+        } else {
+            mStr = &mBuffer[0];
+        }
+        mEnv->GetStringRegion(str, 0, mSize, mStr);
+    }
+    ~StackString() {
+        if (mStr != &mBuffer[0]) {
+            delete[] mStr;
+        }
+    }
+    const jchar* str() { return mStr; }
+    jsize size() { return mSize; }
+
+private:
+    JNIEnv* mEnv;
+    jstring mJStr;
+
+    jchar mBuffer[StackReserve];
+    // pointer to &mBuffer[0] if string fits in mBuffer, otherwise owned
+    jchar* mStr;
+    jsize mSize;
+};
+
+// This size is chosen to be longer than most interface descriptors.
+// Ones longer than this will be allocated on the heap.
+typedef StackString<64> InterfaceDescriptorString;
+
 static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jclass clazz, jlong nativePtr,
                                                   jstring name)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
-    if (parcel != NULL) {
-        // In the current implementation, the token is just the serialized interface name that
-        // the caller expects to be invoking
-        const jchar* str = env->GetStringCritical(name, 0);
-        if (str != NULL) {
-            parcel->writeInterfaceToken(String16(
-                  reinterpret_cast<const char16_t*>(str),
-                  env->GetStringLength(name)));
-            env->ReleaseStringCritical(name, str);
-        }
+    if (parcel != nullptr) {
+        InterfaceDescriptorString descriptor(env, name);
+        parcel->writeInterfaceToken(reinterpret_cast<const char16_t*>(descriptor.str()),
+                                    descriptor.size());
     }
 }
 
 static void android_os_Parcel_enforceInterface(JNIEnv* env, jclass clazz, jlong nativePtr, jstring name)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
-    if (parcel != NULL) {
-        const jchar* str = env->GetStringCritical(name, 0);
-        if (str) {
-            IPCThreadState* threadState = IPCThreadState::self();
-            const int32_t oldPolicy = threadState->getStrictModePolicy();
-            const bool isValid = parcel->enforceInterface(
-                reinterpret_cast<const char16_t*>(str),
-                env->GetStringLength(name),
-                threadState);
-            env->ReleaseStringCritical(name, str);
-            if (isValid) {
-                const int32_t newPolicy = threadState->getStrictModePolicy();
-                if (oldPolicy != newPolicy) {
-                    // Need to keep the Java-level thread-local strict
-                    // mode policy in sync for the libcore
-                    // enforcements, which involves an upcall back
-                    // into Java.  (We can't modify the
-                    // Parcel.enforceInterface signature, as it's
-                    // pseudo-public, and used via AIDL
-                    // auto-generation...)
-                    set_dalvik_blockguard_policy(env, newPolicy);
-                }
-                return;     // everything was correct -> return silently
+    if (parcel != nullptr) {
+        InterfaceDescriptorString descriptor(env, name);
+        IPCThreadState* threadState = IPCThreadState::self();
+        const int32_t oldPolicy = threadState->getStrictModePolicy();
+        const bool isValid =
+                parcel->enforceInterface(reinterpret_cast<const char16_t*>(descriptor.str()),
+                                         descriptor.size(), threadState);
+        if (isValid) {
+            const int32_t newPolicy = threadState->getStrictModePolicy();
+            if (oldPolicy != newPolicy) {
+                // Need to keep the Java-level thread-local strict
+                // mode policy in sync for the libcore
+                // enforcements, which involves an upcall back
+                // into Java.  (We can't modify the
+                // Parcel.enforceInterface signature, as it's
+                // pseudo-public, and used via AIDL
+                // auto-generation...)
+                set_dalvik_blockguard_policy(env, newPolicy);
             }
+            return; // everything was correct -> return silently
         }
     }
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1bf8efb..1419855 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1333,17 +1333,6 @@
     transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
 }
 
-static void nativeDeferTransactionUntilSurface(JNIEnv* env, jclass clazz, jlong transactionObj,
-        jlong nativeObject,
-        jlong surfaceObject, jlong frameNumber) {
-    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
-
-    auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
-    sp<Surface> barrier = reinterpret_cast<Surface *>(surfaceObject);
-
-    transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
-}
-
 static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject,
         jlong newParentObject) {
@@ -1699,8 +1688,6 @@
             (void*)nativeGetProtectedContentSupport },
     {"nativeDeferTransactionUntil", "(JJJJ)V",
             (void*)nativeDeferTransactionUntil },
-    {"nativeDeferTransactionUntilSurface", "(JJJJ)V",
-            (void*)nativeDeferTransactionUntilSurface },
     {"nativeReparentChildren", "(JJJ)V",
             (void*)nativeReparentChildren } ,
     {"nativeReparent", "(JJJ)V",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e6bfecc..42aab6a 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -326,31 +326,15 @@
 static FileDescriptorTable* gOpenFdTable = nullptr;
 
 // Must match values in com.android.internal.os.Zygote.
-// The order of entries here must be kept in sync with ExternalStorageViews array values.
+// Note that there are gaps in the constants:
+// This is to further keep the values consistent with IVold.aidl
 enum MountExternalKind {
-  MOUNT_EXTERNAL_NONE = 0,
-  MOUNT_EXTERNAL_DEFAULT = 1,
-  MOUNT_EXTERNAL_READ = 2,
-  MOUNT_EXTERNAL_WRITE = 3,
-  MOUNT_EXTERNAL_LEGACY = 4,
-  MOUNT_EXTERNAL_INSTALLER = 5,
-  MOUNT_EXTERNAL_FULL = 6,
-  MOUNT_EXTERNAL_PASS_THROUGH = 7,
-  MOUNT_EXTERNAL_ANDROID_WRITABLE = 8,
-  MOUNT_EXTERNAL_COUNT = 9
-};
-
-// The order of entries here must be kept in sync with MountExternalKind enum values.
-static const std::array<const std::string, MOUNT_EXTERNAL_COUNT> ExternalStorageViews = {
-  "",                     // MOUNT_EXTERNAL_NONE
-  "/mnt/runtime/default", // MOUNT_EXTERNAL_DEFAULT
-  "/mnt/runtime/read",    // MOUNT_EXTERNAL_READ
-  "/mnt/runtime/write",   // MOUNT_EXTERNAL_WRITE
-  "/mnt/runtime/write",   // MOUNT_EXTERNAL_LEGACY
-  "/mnt/runtime/write",   // MOUNT_EXTERNAL_INSTALLER
-  "/mnt/runtime/full",    // MOUNT_EXTERNAL_FULL
-  "/mnt/runtime/full",    // MOUNT_EXTERNAL_PASS_THROUGH (only used w/ FUSE)
-  "/mnt/runtime/full",    // MOUNT_EXTERNAL_ANDROID_WRITABLE (only used w/ FUSE)
+    MOUNT_EXTERNAL_NONE = 0,
+    MOUNT_EXTERNAL_DEFAULT = 1,
+    MOUNT_EXTERNAL_INSTALLER = 5,
+    MOUNT_EXTERNAL_PASS_THROUGH = 7,
+    MOUNT_EXTERNAL_ANDROID_WRITABLE = 8,
+    MOUNT_EXTERNAL_COUNT = 9
 };
 
 // Must match values in com.android.internal.os.Zygote.
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index d4d8772..d315ff2 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -205,9 +205,8 @@
     optional WindowStateProto input_method_input_target = 28;
     optional WindowStateProto input_method_control_target = 29;
     optional WindowStateProto current_focus = 30;
-    optional InsetsSourceProviderProto insets_source_provider = 31;
-    optional ImeInsetsSourceProviderProto ime_insets_source_provider = 32;
-    optional bool can_show_ime = 33;
+    optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31;
+    optional bool can_show_ime = 32;
 }
 
 /* represents DisplayArea object */
@@ -527,6 +526,7 @@
 message ImeInsetsSourceProviderProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional WindowStateProto ime_target_from_ime = 1;
-    optional bool is_ime_layout_drawn = 2;
+    optional InsetsSourceProviderProto insets_source_provider = 1;
+    optional WindowStateProto ime_target_from_ime = 2;
+    optional bool is_ime_layout_drawn = 3;
 }
\ No newline at end of file
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 2729e82..856d8da 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -65,8 +65,6 @@
         repeated ClientSideProto client = 1;
     }
 
-    /* todo: extract as a separate message to allow other dumps to use this as their client side
-        proto */
     /* groups together the dump from ime related client side classes */
     message ClientSideProto {
         optional int32 display_id = 1;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57c25f1..c8b8ef2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2722,7 +2722,7 @@
     <!-- Allows applications like settings to manage configuration associated with automatic time
          and time zone detection.
          <p>Not for use by third-party applications.
-         @hide
+         @SystemApi @hide
     -->
     <permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION"
         android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 60f7a09..fac2f22 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</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">"This app can record audio using the microphone while the app is in use."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</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">"This app can take pictures and record videos using the camera while the app is in use."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index ae3aaab..8497658 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</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">"This app can record audio using the microphone while the app is in use."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</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">"This app can take pictures and record videos using the camera while the app is in use."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 52dcf97..12881e7 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</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">"This app can record audio using the microphone while the app is in use."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</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">"This app can take pictures and record videos using the camera while the app is in use."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 2f7f271..cb2eb7a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"change your audio settings"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Allows the app to modify global audio settings such as volume and which speaker is used for output."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"record audio"</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">"This app can record audio using the microphone while the app is in use."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"record audio in the background"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"This app can record audio using the microphone at any time."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"send commands to the SIM"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Allows the app to send commands to the SIM. This is very dangerous."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"recognise physical activity"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"This app can recognise your physical activity."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"take pictures and videos"</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">"This app can take pictures and record videos using the camera while the app is in use."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"take pictures and videos in the background"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"This app can take pictures and record videos using the camera at any time."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Grant an application or service access to system cameras to take pictures and videos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"This privileged or system app can take pictures and record videos using a system camera at any time. Requires the android.permission.CAMERA permission to be held by the app as well"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Allow an application or service to receive callbacks about camera devices being opened or closed."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index f09e7b0..450fddb 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar áudio"</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">"Enquanto está sendo usado, este app pode gravar áudio usando o microfone."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar áudio em segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos para o chip"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconhecer atividade física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Este app pode reconhecer sua atividade física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"tirar fotos e gravar vídeos"</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">"Enquanto está sendo usado, este app pode tirar fotos e gravar vídeos usando a câmera."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tirar fotos e gravar vídeos em segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que um aplicativo ou serviço acesse as câmeras do sistema para tirar fotos e gravar vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index f09e7b0..450fddb 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -432,23 +432,17 @@
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
     <string name="permlab_recordAudio" msgid="1208457423054219147">"gravar áudio"</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">"Enquanto está sendo usado, este app pode gravar áudio usando o microfone."</string>
+    <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"gravar áudio em segundo plano"</string>
+    <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
     <string name="permlab_sim_communication" msgid="176788115994050692">"enviar comandos para o chip"</string>
     <string name="permdesc_sim_communication" msgid="4179799296415957960">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
     <string name="permlab_activityRecognition" msgid="1782303296053990884">"reconhecer atividade física"</string>
     <string name="permdesc_activityRecognition" msgid="8667484762991357519">"Este app pode reconhecer sua atividade física."</string>
     <string name="permlab_camera" msgid="6320282492904119413">"tirar fotos e gravar vídeos"</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">"Enquanto está sendo usado, este app pode tirar fotos e gravar vídeos usando a câmera."</string>
+    <string name="permlab_backgroundCamera" msgid="7549917926079731681">"tirar fotos e gravar vídeos em segundo plano"</string>
+    <string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
     <string name="permlab_systemCamera" msgid="3642917457796210580">"Permitir que um aplicativo ou serviço acesse as câmeras do sistema para tirar fotos e gravar vídeos"</string>
     <string name="permdesc_systemCamera" msgid="5938360914419175986">"Esse app do sistema ou com privilégios pode tirar fotos e gravar vídeos a qualquer momento usando a câmera do sistema. É necessário que o app tenha também a permissão android.permission.CAMERA"</string>
     <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permitir que um aplicativo ou serviço receba callbacks sobre dispositivos de câmera sendo abertos ou fechados."</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 96ebc12..25c64a9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2016,6 +2016,22 @@
         <attr name="requiredNotFeature" format="string" />
     </declare-styleable>
 
+    <!-- <code>required-feature</code> and <code>required-not-feature</code> elements inside
+         <code>uses-permission<code/> can be used to request the permission based on the fact
+         whether the system supports or does not support certain features.
+         If multiple <code>required-feature</code> and/or <code>required-not-feature</code> elements
+         are present, the permission will be “requested” only if the system supports all of the
+         listed "required-features" and does not support any of the "required-not-features".
+         -->
+    <declare-styleable name="AndroidManifestRequiredFeature">
+        <!-- The name of the feature. -->
+        <attr name="name" />
+    </declare-styleable>
+    <declare-styleable name="AndroidManifestRequiredNotFeature">
+        <!-- The name of the feature. -->
+        <attr name="name" />
+    </declare-styleable>
+
     <!-- The <code>uses-configuration</code> tag specifies
          a specific hardware configuration value used by the application.
          For example an application might specify that it requires
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71e9ed7..0cfbf6d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1588,6 +1588,16 @@
          config_enablePrimaryLocationTimeZoneOverlay is false. -->
     <string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
 
+    <!-- Whether to enable secondary location time zone provider overlay which allows the secondary
+         location time zone provider to be replaced by an app at run-time. When disabled, only the
+         config_secondaryLocationTimeZoneProviderPackageName package will be searched for the
+         secondary location time zone provider, otherwise any system package is eligible. Anyone who
+         wants to disable the overlay mechanism can set it to false. -->
+    <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
+    <!-- Package name providing the secondary location time zone provider. Used only when
+         config_enableSecondaryLocationTimeZoneOverlay is false. -->
+    <string name="config_secondaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
+
     <!-- Whether to enable network location overlay which allows network location provider to be
          replaced by an app at run-time. When disabled, only the
          config_networkLocationProviderPackageName package will be searched for network location
@@ -1831,6 +1841,8 @@
     <string name="config_systemGallery" translatable="false">com.android.gallery3d</string>
     <!-- The name of the package that will hold the system cluster service role. -->
     <string name="config_systemAutomotiveCluster" translatable="false"></string>
+    <!-- The name of the package that will hold the system video call role. -->
+    <string name="config_systemVideoCall" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false"></string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1e2d554..fe17eca 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3071,6 +3071,8 @@
   <public-group type="string" first-id="0x01040028">
     <!-- @hide @SystemApi @TestApi -->
     <public name="config_systemAutomotiveCluster" />
+    <!-- @hide @SystemApi @TestApi -->
+    <public name="config_systemVideoCall" />
   </public-group>
 
   <public-group type="id" first-id="0x01020055">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3e59549..d896a26 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2160,6 +2160,8 @@
   <java-symbol type="string" name="config_deviceConfiguratorPackageName" />
   <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
   <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
+  <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
+  <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
 
   <java-symbol type="layout" name="resolver_list" />
   <java-symbol type="id" name="resolver_list" />
@@ -3545,6 +3547,7 @@
   <java-symbol type="id" name="bubble_button" />
   <java-symbol type="dimen" name="bubble_visible_padding_end" />
   <java-symbol type="dimen" name="bubble_gone_padding_end" />
+  <java-symbol type="dimen" name="text_size_body_2_material" />
   <java-symbol type="dimen" name="messaging_avatar_size" />
   <java-symbol type="dimen" name="messaging_group_sending_progress_size" />
   <java-symbol type="dimen" name="messaging_image_rounding" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 8b25afb..cb20130 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -51,12 +51,14 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.view.Display;
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
@@ -78,6 +80,8 @@
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
+@Presubmit
+@FlakyTest(detail = "Promote once confirmed non-flaky")
 public class ActivityThreadTest {
     private static final int TIMEOUT_SEC = 10;
 
@@ -545,14 +549,15 @@
         rIntents.add(new ReferrerIntent(new Intent(), "android.app.activity"));
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
-        });
-        assertThat(activity.isResumed()).isFalse();
-
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, true));
         });
         assertThat(activity.isResumed()).isTrue();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            activityThread.executeTransaction(newStopTransaction(activity));
+            activityThread.executeTransaction(newNewIntentTransaction(activity, rIntents, false));
+        });
+        assertThat(activity.isResumed()).isFalse();
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index b669cc6..a9cfd28 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -17,8 +17,10 @@
 package com.android.internal.jank;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_TO_STATSD_INTERACTION_TYPE;
 
 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.anyLong;
@@ -45,6 +47,13 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
 @SmallTest
 public class InteractionJankMonitorTest {
     private ViewAttachTestActivity mActivity;
@@ -138,4 +147,27 @@
         verify(tracker).cancel();
     }
 
+    @Test
+    public void testCujTypeEnumCorrectlyDefined() throws Exception {
+        List<Field> cujEnumFields =
+                Arrays.stream(InteractionJankMonitor.class.getDeclaredFields())
+                        .filter(field -> field.getName().startsWith("CUJ_")
+                                && Modifier.isStatic(field.getModifiers())
+                                && field.getType() == int.class)
+                        .collect(Collectors.toList());
+
+        HashSet<Integer> allValues = new HashSet<>();
+        for (Field field : cujEnumFields) {
+            int fieldValue = field.getInt(null);
+            assertWithMessage(
+                    "Field %s must have a mapping to a value in CUJ_TO_STATSD_INTERACTION_TYPE",
+                    field.getName())
+                    .that(fieldValue < CUJ_TO_STATSD_INTERACTION_TYPE.length)
+                    .isTrue();
+            assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
+                    field.getName())
+                    .that(allValues.add(fieldValue))
+                    .isTrue();
+        }
+    }
 }
diff --git a/core/xsd/vts/Android.bp b/core/xsd/vts/Android.bp
index 4b43b41..ca655f1 100644
--- a/core/xsd/vts/Android.bp
+++ b/core/xsd/vts/Android.bp
@@ -40,7 +40,3 @@
     ],
     test_config: "vts_permission_validate_test.xml",
 }
-
-vts_config {
-    name: "VtsValidatePermission",
-}
diff --git a/core/xsd/vts/AndroidTest.xml b/core/xsd/vts/AndroidTest.xml
deleted file mode 100644
index e5cc9a0..0000000
--- a/core/xsd/vts/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for VTS VtsValidatePermission.">
-    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-        <option name="push" value="DATA/etc/permission.xsd->/data/local/tmp/permission.xsd"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-        <option name="test-module-name" value="VtsValidatePermission"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_permission_validate_test/vts_permission_validate_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_permission_validate_test/vts_permission_validate_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="test-timeout" value="30s"/>
-    </test>
-</configuration>
diff --git a/data/etc/com.android.storagemanager.xml b/data/etc/com.android.storagemanager.xml
index e85a82c..a1635fe 100644
--- a/data/etc/com.android.storagemanager.xml
+++ b/data/etc/com.android.storagemanager.xml
@@ -22,5 +22,6 @@
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
     </privapp-permissions>
 </permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 29c8de6..b94721e 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -115,12 +115,6 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
     },
-    "-1977793524": {
-      "message": "moveStackToDisplay: moving stackId=%d to displayId=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_TASKS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "-1976930686": {
       "message": "Attempted to add Accessibility overlay window with bad token %s.  Aborting.",
       "level": "WARN",
@@ -583,6 +577,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1419762046": {
+      "message": "moveRootTaskToDisplay: moving taskId=%d to displayId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1413901262": {
       "message": "startRecentsActivity(): intent=%s",
       "level": "DEBUG",
@@ -1183,6 +1183,12 @@
       "group": "WM_DEBUG_SYNC_ENGINE",
       "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
     },
+    "-677449371": {
+      "message": "moveTaskToRootTask: moving task=%d to rootTaskId=%d toTop=%b",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-672228342": {
       "message": "resumeTopActivityLocked: Top activity resumed %s",
       "level": "DEBUG",
@@ -1555,12 +1561,6 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
-    "-300896109": {
-      "message": "moveTaskToStack: moving task=%d to stackId=%d toTop=%b",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_TASKS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "-279436615": {
       "message": "Moving to PAUSING: %s",
       "level": "VERBOSE",
@@ -1999,6 +1999,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "255339989": {
+      "message": "setFocusedRootTask: taskId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "255692476": {
       "message": "**** GOOD TO GO",
       "level": "VERBOSE",
@@ -3361,12 +3367,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
-    "1975793405": {
-      "message": "setFocusedStack: stackId=%d",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
new file mode 100644
index 0000000..68477ed
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/BinderIdentityChecker.java
@@ -0,0 +1,108 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.contains;
+import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.staticMethod;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.VariableTree;
+
+import java.util.List;
+
+import javax.lang.model.element.Modifier;
+
+/**
+ * Binder maintains thread-local identity information about any remote caller,
+ * which can be temporarily cleared while performing operations that need to be
+ * handled as the current process. However, it's important to restore the
+ * original remote calling identity after carefully scoping this work inside a
+ * try/finally block, to avoid obscure security vulnerabilities.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkBinderIdentity",
+    summary = "Verifies that Binder.clearCallingIdentity() is always restored",
+    severity = WARNING)
+public final class BinderIdentityChecker extends BugChecker implements MethodInvocationTreeMatcher {
+    private static final Matcher<ExpressionTree> CLEAR_CALL = methodInvocation(staticMethod()
+            .onClass("android.os.Binder").withSignature("clearCallingIdentity()"));
+    private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod()
+            .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)"));
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (CLEAR_CALL.matches(tree, state)) {
+            // First, make sure we're recording the token for later
+            final VariableTree token = state.findEnclosing(VariableTree.class);
+            if (token == null || !token.getModifiers().getFlags().contains(Modifier.FINAL)) {
+                return buildDescription(tree)
+                        .setMessage("Must store Binder.clearCallingIdentity() token as final"
+                                + " variable to support safe restore")
+                        .build();
+            }
+
+            // Next, verify the very next block is try-finally; any other calls
+            // between the clearing and try risk throwing an exception without
+            // doing a safe restore
+            final Tree next = nextStatement(token, state);
+            if (next == null || next.getKind() != Kind.TRY) {
+                return buildDescription(tree)
+                        .setMessage("Must immediately define a try-finally block after"
+                                + " Binder.clearCallingIdentity() to support safe restore")
+                        .build();
+            }
+
+            // Finally, verify that we restore inside the finally block
+            final TryTree tryTree = (TryTree) next;
+            final BlockTree finallyTree = tryTree.getFinallyBlock();
+            if (finallyTree == null
+                    || !contains(ExpressionTree.class, RESTORE_CALL).matches(finallyTree, state)) {
+                return buildDescription(tree)
+                        .setMessage("Must call Binder.restoreCallingIdentity() in finally"
+                                + "  block to support safe restore")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+
+    private static Tree nextStatement(Tree tree, VisitorState state) {
+        final BlockTree block = state.findEnclosing(BlockTree.class);
+        if (block == null) return null;
+        final List<? extends StatementTree> siblings = block.getStatements();
+        if (siblings == null) return null;
+        final int index = siblings.indexOf(tree);
+        if (index == -1 || index + 1 >= siblings.size()) return null;
+        return siblings.get(index + 1);
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
index 8829b0d..9c84f50 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/CompatChangeChecker.java
@@ -17,12 +17,13 @@
 package com.google.errorprone.bugpatterns.android;
 
 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.bugpatterns.android.TargetSdkChecker.binaryTreeExact;
 import static com.google.errorprone.matchers.FieldMatchers.anyFieldInClass;
 import static com.google.errorprone.matchers.FieldMatchers.staticField;
 import static com.google.errorprone.matchers.Matchers.allOf;
 import static com.google.errorprone.matchers.Matchers.anyOf;
 import static com.google.errorprone.matchers.Matchers.anything;
-import static com.google.errorprone.matchers.Matchers.binaryTree;
+import static com.google.errorprone.matchers.Matchers.kindIs;
 import static com.google.errorprone.matchers.Matchers.not;
 
 import com.google.auto.service.AutoService;
@@ -34,6 +35,7 @@
 import com.google.errorprone.matchers.Matcher;
 import com.sun.source.tree.BinaryTree;
 import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.Tree.Kind;
 
 /**
  * Each SDK level often has dozens of different behavior changes, which can be
@@ -85,14 +87,28 @@
             staticField("android.os.Build.VERSION_CODES", "O"),
             staticField("android.os.Build.VERSION_CODES", "O_MR1"),
             staticField("android.os.Build.VERSION_CODES", "P"),
-            staticField("android.os.Build.VERSION_CODES", "Q"));
+            staticField("android.os.Build.VERSION_CODES", "Q"),
+            staticField("android.os.Build.VERSION_CODES", "R"));
+
+    private static final Matcher<ExpressionTree> R_VERSION_CODE =
+            staticField("android.os.Build.VERSION_CODES", "R");
+
+    private static final Matcher<ExpressionTree> CUR_DEVELOPMENT_VERSION_CODE =
+            staticField("android.os.Build.VERSION_CODES", "CUR_DEVELOPMENT");
 
     private static final Matcher<ExpressionTree> MODERN_VERSION_CODE =
-            allOf(VERSION_CODE, not(LEGACY_VERSION_CODE));
+            allOf(VERSION_CODE, not(LEGACY_VERSION_CODE), not(CUR_DEVELOPMENT_VERSION_CODE));
+
+    private static final Matcher<ExpressionTree> BOOLEAN_OPERATOR = anyOf(
+            kindIs(Kind.LESS_THAN), kindIs(Kind.LESS_THAN_EQUAL),
+            kindIs(Kind.GREATER_THAN), kindIs(Kind.GREATER_THAN_EQUAL),
+            kindIs(Kind.EQUAL_TO), kindIs(Kind.NOT_EQUAL_TO));
 
     private static final Matcher<BinaryTree> INVALID = anyOf(
-            binaryTree(MODERN_VERSION_CODE, anything()),
-            binaryTree(anything(), MODERN_VERSION_CODE));
+            allOf(BOOLEAN_OPERATOR, binaryTreeExact(MODERN_VERSION_CODE, anything())),
+            allOf(BOOLEAN_OPERATOR, binaryTreeExact(anything(), MODERN_VERSION_CODE)),
+            allOf(kindIs(Kind.GREATER_THAN), binaryTreeExact(anything(), R_VERSION_CODE)),
+            allOf(kindIs(Kind.LESS_THAN), binaryTreeExact(R_VERSION_CODE, anything())));
 
     @Override
     public Description matchBinary(BinaryTree tree, VisitorState state) {
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
index 4f1af3e..3a1bc1e 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/ContextUserIdChecker.java
@@ -18,6 +18,7 @@
 
 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
 import static com.google.errorprone.bugpatterns.android.UidChecker.getFlavor;
+import static com.google.errorprone.matchers.Matchers.anyOf;
 import static com.google.errorprone.matchers.Matchers.enclosingClass;
 import static com.google.errorprone.matchers.Matchers.hasAnnotation;
 import static com.google.errorprone.matchers.Matchers.instanceMethod;
@@ -60,8 +61,13 @@
 
     private static final Matcher<ExpressionTree> BINDER_CALL = methodInvocation(
             instanceMethod().onDescendantOf("android.os.IInterface").withAnyName());
-    private static final Matcher<ExpressionTree> GET_USER_ID_CALL = methodInvocation(
-            instanceMethod().onDescendantOf("android.content.Context").named("getUserId"));
+    private static final Matcher<ExpressionTree> GET_USER_ID_CALL = methodInvocation(anyOf(
+            instanceMethod().onExactClass("android.app.admin.DevicePolicyManager")
+                    .named("myUserId"),
+            instanceMethod().onExactClass("android.content.pm.ShortcutManager")
+                    .named("injectMyUserId"),
+            instanceMethod().onDescendantOf("android.content.Context")
+                    .named("getUserId")));
 
     private static final Matcher<ExpressionTree> USER_ID_FIELD = new Matcher<ExpressionTree>() {
         @Override
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
new file mode 100644
index 0000000..c4c1ab6
--- /dev/null
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsChecker.java
@@ -0,0 +1,98 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Android offers several efficient alternatives to some upstream
+ * {@link Collections} containers, such as {@code SparseIntArray} instead of
+ * {@code Map<Integer, Integer>}.
+ */
+@AutoService(BugChecker.class)
+@BugPattern(
+    name = "AndroidFrameworkEfficientCollections",
+    summary = "Verifies efficient collections best-practices",
+    severity = WARNING)
+public final class EfficientCollectionsChecker extends BugChecker implements NewClassTreeMatcher {
+    private static final Matcher<Tree> IS_LIST = isSubtypeOf("java.util.List");
+    private static final Matcher<Tree> IS_MAP = isSubtypeOf("java.util.Map");
+
+    private static final String INTEGER = "java.lang.Integer";
+    private static final String LONG = "java.lang.Long";
+    private static final String BOOLEAN = "java.lang.Boolean";
+
+    @Override
+    public Description matchNewClass(NewClassTree tree, VisitorState state) {
+        final List<Type> types = ASTHelpers.getType(tree).getTypeArguments();
+        if (IS_LIST.matches(tree, state) && types != null && types.size() == 1) {
+            final Type first = types.get(0);
+            if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with IntArray for efficiency")
+                        .build();
+            } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state))  {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with LongArray for efficiency")
+                        .build();
+            }
+        } else if (IS_MAP.matches(tree, state) && types != null && types.size() == 2) {
+            final Type first = types.get(0);
+            final Type second = types.get(1);
+            if (ASTHelpers.isSameType(first, state.getTypeFromString(INTEGER), state)) {
+                if (ASTHelpers.isSameType(second, state.getTypeFromString(INTEGER), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseIntArray for efficiency")
+                            .build();
+                } else if (ASTHelpers.isSameType(second, state.getTypeFromString(LONG), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseLongArray for efficiency")
+                            .build();
+                } else if (ASTHelpers.isSameType(second, state.getTypeFromString(BOOLEAN), state)) {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseBooleanArray for efficiency")
+                            .build();
+                } else {
+                    return buildDescription(tree)
+                            .setMessage("Consider replacing with SparseArray for efficiency")
+                            .build();
+                }
+            } else if (ASTHelpers.isSameType(first, state.getTypeFromString(LONG), state)) {
+                return buildDescription(tree)
+                        .setMessage("Consider replacing with LongSparseArray for efficiency")
+                        .build();
+            }
+        }
+        return Description.NO_MATCH;
+    }
+}
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
index 48123ab..130b256 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemChecker.java
@@ -17,11 +17,14 @@
 package com.google.errorprone.bugpatterns.android;
 
 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+import static com.google.errorprone.matchers.Matchers.allOf;
+import static com.google.errorprone.matchers.Matchers.contains;
 import static com.google.errorprone.matchers.Matchers.enclosingClass;
 import static com.google.errorprone.matchers.Matchers.hasAnnotation;
 import static com.google.errorprone.matchers.Matchers.instanceMethod;
 import static com.google.errorprone.matchers.Matchers.isSameType;
 import static com.google.errorprone.matchers.Matchers.methodInvocation;
+import static com.google.errorprone.matchers.Matchers.not;
 import static com.google.errorprone.matchers.Matchers.throwStatement;
 import static com.google.errorprone.matchers.Matchers.variableType;
 
@@ -29,13 +32,17 @@
 import com.google.errorprone.BugPattern;
 import com.google.errorprone.VisitorState;
 import com.google.errorprone.bugpatterns.BugChecker;
-import com.google.errorprone.bugpatterns.BugChecker.CatchTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.TryTreeMatcher;
 import com.google.errorprone.matchers.Description;
 import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.predicates.TypePredicate;
 import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.StatementTree;
 import com.sun.source.tree.Tree;
+import com.sun.source.tree.TryTree;
 import com.sun.source.tree.VariableTree;
+import com.sun.tools.javac.code.Type;
 
 import java.util.List;
 
@@ -54,9 +61,17 @@
     name = "AndroidFrameworkRethrowFromSystem",
     summary = "Verifies that system_server calls use rethrowFromSystemServer()",
     severity = WARNING)
-public final class RethrowFromSystemChecker extends BugChecker implements CatchTreeMatcher {
+public final class RethrowFromSystemChecker extends BugChecker implements TryTreeMatcher {
     private static final Matcher<Tree> INSIDE_MANAGER =
             enclosingClass(hasAnnotation("android.annotation.SystemService"));
+
+    // Purposefully exclude telephony Binder interfaces, since we know they
+    // always run under the separate AID_RADIO
+    private static final Matcher<ExpressionTree> SYSTEM_BINDER_CALL = methodInvocation(allOf(
+            instanceMethod().onDescendantOf("android.os.IInterface").withAnyName(),
+            not(instanceMethod().onClass(inPackage("com.android.internal.telephony"))),
+            not(instanceMethod().onClass(inPackage("com.android.internal.telecom")))));
+
     private static final Matcher<VariableTree> REMOTE_EXCEPTION = variableType(
             isSameType("android.os.RemoteException"));
     private static final Matcher<StatementTree> RETHROW_FROM_SYSTEM = throwStatement(
@@ -64,17 +79,33 @@
                     .named("rethrowFromSystemServer")));
 
     @Override
-    public Description matchCatch(CatchTree tree, VisitorState state) {
+    public Description matchTry(TryTree tree, VisitorState state) {
         if (INSIDE_MANAGER.matches(tree, state)
-                && REMOTE_EXCEPTION.matches(tree.getParameter(), state)) {
-            final List<? extends StatementTree> statements = tree.getBlock().getStatements();
-            if (statements.size() != 1 || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) {
-                return buildDescription(tree)
-                        .setMessage("Must contain single "
-                                + "'throw e.rethrowFromSystemServer()' statement")
-                        .build();
+                && contains(ExpressionTree.class, SYSTEM_BINDER_CALL)
+                        .matches(tree.getBlock(), state)) {
+            for (CatchTree catchTree : tree.getCatches()) {
+                if (REMOTE_EXCEPTION.matches(catchTree.getParameter(), state)) {
+                    final List<? extends StatementTree> statements = catchTree.getBlock()
+                            .getStatements();
+                    if (statements.size() != 1
+                            || !RETHROW_FROM_SYSTEM.matches(statements.get(0), state)) {
+                        return buildDescription(catchTree)
+                                .setMessage("Must contain single "
+                                        + "'throw e.rethrowFromSystemServer()' statement")
+                                .build();
+                    }
+                }
             }
         }
         return Description.NO_MATCH;
     }
+
+    private static TypePredicate inPackage(final String filter) {
+        return new TypePredicate() {
+            @Override
+            public boolean apply(Type type, VisitorState state) {
+                return type.tsym.packge().fullname.toString().startsWith(filter);
+            }
+        };
+    }
 }
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
index 232cf3f..e1ebf42 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/TargetSdkChecker.java
@@ -89,7 +89,7 @@
         return Description.NO_MATCH;
     }
 
-    private static Matcher<BinaryTree> binaryTreeExact(Matcher<ExpressionTree> left,
+    static Matcher<BinaryTree> binaryTreeExact(Matcher<ExpressionTree> left,
             Matcher<ExpressionTree> right) {
         return new Matcher<BinaryTree>() {
             @Override
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java
new file mode 100644
index 0000000..9448344
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/BinderIdentityCheckerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BinderIdentityCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                BinderIdentityChecker.class, getClass());
+    }
+
+    @Test
+    public void testValid() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceLines("FooService.java",
+                        "import android.os.Binder;",
+                        "public class FooService {",
+                        "  void bar() {",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testInvalid() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceLines("FooService.java",
+                        "import android.os.Binder;",
+                        "public class FooService {",
+                        "  void noRestore() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "  }",
+                        "  void noTry() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "    Binder.restoreCallingIdentity(token);",
+                        "  }",
+                        "  void noImmediateTry() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "  void noFinally() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } catch (Exception ignored) { }",
+                        "  }",
+                        "  void noFinal() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    long token = Binder.clearCallingIdentity();",
+                        "    try {",
+                        "      FooService.class.toString();",
+                        "    } finally {",
+                        "      Binder.restoreCallingIdentity(token);",
+                        "    }",
+                        "  }",
+                        "  void noRecording() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    Binder.clearCallingIdentity();",
+                        "    FooService.class.toString();",
+                        "  }",
+                        "  void noWork() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    final long token = Binder.clearCallingIdentity();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java
index 105633e..4625d43 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/CompatChangeCheckerTest.java
@@ -42,17 +42,17 @@
                         "public class Example {",
                         "  void test(int targetSdkVersion) {",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion < Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion < Build.VERSION_CODES.S) { }",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion <= Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion <= Build.VERSION_CODES.S) { }",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion > Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion > Build.VERSION_CODES.S) { }",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion >= Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion >= Build.VERSION_CODES.S) { }",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion == Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion == Build.VERSION_CODES.S) { }",
                         "    // BUG: Diagnostic contains:",
-                        "    if (targetSdkVersion != Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion != Build.VERSION_CODES.S) { }",
                         "  }",
                         "}")
                 .doTest();
@@ -63,18 +63,29 @@
         compilationHelper
                 .addSourceFile("/android/os/Build.java")
                 .addSourceLines("Example.java",
-                        "import static android.os.Build.VERSION_CODES.R;",
+                        "import android.os.Build;",
+                        "import static android.os.Build.VERSION_CODES.S;",
                         "public class Example {",
                         "  void test(int targetSdkVersion) {",
                         "    // BUG: Diagnostic contains:",
-                        "    boolean indirect = R > targetSdkVersion;",
+                        "    boolean indirect = S >= targetSdkVersion;",
+                        "    // BUG: Diagnostic contains:",
+                        "    if (targetSdkVersion > Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion >= Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion < Build.VERSION_CODES.R) { }",
+                        "    if (targetSdkVersion <= Build.VERSION_CODES.R) { }",
+                        "    // BUG: Diagnostic contains:",
+                        "    if (Build.VERSION_CODES.R < targetSdkVersion) { }",
+                        "    if (Build.VERSION_CODES.R <= targetSdkVersion) { }",
+                        "    if (Build.VERSION_CODES.R > targetSdkVersion) { }",
+                        "    if (Build.VERSION_CODES.R >= targetSdkVersion) { }",
                         "  }",
                         "}")
                 .doTest();
     }
 
     @Test
-    public void testLegacyIgnored() {
+    public void testIgnored() {
         compilationHelper
                 .addSourceFile("/android/os/Build.java")
                 .addSourceLines("Example.java",
@@ -82,6 +93,8 @@
                         "public class Example {",
                         "  void test(int targetSdkVersion) {",
                         "    if (targetSdkVersion < Build.VERSION_CODES.DONUT) { }",
+                        "    String result = \"test\" + Build.VERSION_CODES.S;",
+                        "    if (targetSdkVersion != Build.VERSION_CODES.CUR_DEVELOPMENT) { }",
                         "  }",
                         "}")
                 .doTest();
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java
index c0b8cd7..c877230 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java
@@ -92,4 +92,56 @@
                         "}")
                 .doTest();
     }
+
+    @Test
+    public void testDevicePolicyManager() {
+        compilationHelper
+                .addSourceFile("/android/annotation/SystemService.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/foo/IFooService.java")
+                .addSourceFile("/android/os/IInterface.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/RemoteException.java")
+                .addSourceLines("DevicePolicyManager.java",
+                        "package android.app.admin;",
+                        "import android.annotation.SystemService;",
+                        "import android.content.Context;",
+                        "import android.foo.IFooService;",
+                        "import android.os.UserHandle;",
+                        "import android.os.RemoteException;",
+                        "@SystemService(\"dp\") public class DevicePolicyManager {",
+                        "  IFooService mService;",
+                        "  int myUserId() { return 0; }",
+                        "  void bar() throws RemoteException {",
+                        "    mService.baz(null, myUserId());",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testShortcutManager() {
+        compilationHelper
+                .addSourceFile("/android/annotation/SystemService.java")
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/foo/IFooService.java")
+                .addSourceFile("/android/os/IInterface.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceFile("/android/os/RemoteException.java")
+                .addSourceLines("ShortcutManager.java",
+                        "package android.content.pm;",
+                        "import android.annotation.SystemService;",
+                        "import android.content.Context;",
+                        "import android.foo.IFooService;",
+                        "import android.os.UserHandle;",
+                        "import android.os.RemoteException;",
+                        "@SystemService(\"shortcut\") public class ShortcutManager {",
+                        "  IFooService mService;",
+                        "  int injectMyUserId() { return 0; }",
+                        "  void bar() throws RemoteException {",
+                        "    mService.baz(null, injectMyUserId());",
+                        "  }",
+                        "}")
+                .doTest();
+    }
 }
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
new file mode 100644
index 0000000..e128b6a
--- /dev/null
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientCollectionsCheckerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.google.errorprone.bugpatterns.android;
+
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EfficientCollectionsCheckerTest {
+    private CompilationTestHelper compilationHelper;
+
+    @Before
+    public void setUp() {
+        compilationHelper = CompilationTestHelper.newInstance(
+                EfficientCollectionsChecker.class, getClass());
+    }
+
+    @Test
+    public void testMap() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.HashMap;",
+                        "public class Example {",
+                        "  public void exampleInteger() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Integer> a = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Long> b = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, Boolean> c = new HashMap<>();",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Integer, String> d = new HashMap<>();",
+                        "  }",
+                        "  public void exampleLong() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    HashMap<Long, String> res = new HashMap<>();",
+                        "  }",
+                        "  public void exampleOther() {",
+                        "    HashMap<String, String> res = new HashMap<>();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testList() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.ArrayList;",
+                        "public class Example {",
+                        "  public void exampleInteger() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    ArrayList<Integer> res = new ArrayList<>();",
+                        "  }",
+                        "  public void exampleLong() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    ArrayList<Long> res = new ArrayList<>();",
+                        "  }",
+                        "  public void exampleOther() {",
+                        "    ArrayList<String> res = new ArrayList<>();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testErasure() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "import java.util.HashMap;",
+                        "public class Example {",
+                        "  public void example() {",
+                        "    HashMap a = new HashMap();",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+}
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java
index 32efbf2..0943bd6 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RethrowFromSystemCheckerTest.java
@@ -105,4 +105,27 @@
                         "}")
                 .doTest();
     }
+
+    @Test
+    public void testTelephony() {
+        compilationHelper
+                .addSourceFile("/android/annotation/SystemService.java")
+                .addSourceFile("/com/android/internal/telephony/ITelephony.java")
+                .addSourceFile("/android/os/IInterface.java")
+                .addSourceFile("/android/os/RemoteException.java")
+                .addSourceLines("TelephonyManager.java",
+                        "import android.annotation.SystemService;",
+                        "import com.android.internal.telephony.ITelephony;",
+                        "import android.os.RemoteException;",
+                        "@SystemService(\"telephony\") public class TelephonyManager {",
+                        "  ITelephony mService;",
+                        "  void bar() {",
+                        "    try {",
+                        "      mService.bar();",
+                        "    } catch (RemoteException ignored) {",
+                        "    }",
+                        "  }",
+                        "}")
+                .doTest();
+    }
 }
diff --git a/errorprone/tests/res/android/os/Binder.java b/errorprone/tests/res/android/os/Binder.java
index d388587..c969108 100644
--- a/errorprone/tests/res/android/os/Binder.java
+++ b/errorprone/tests/res/android/os/Binder.java
@@ -20,4 +20,12 @@
     public static int getCallingUid() {
         throw new UnsupportedOperationException();
     }
+
+    public static long clearCallingIdentity() {
+        throw new UnsupportedOperationException();
+    }
+
+    public static void restoreCallingIdentity(long token) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/errorprone/tests/res/android/os/Build.java b/errorprone/tests/res/android/os/Build.java
index 0b51bdb..2d354e2 100644
--- a/errorprone/tests/res/android/os/Build.java
+++ b/errorprone/tests/res/android/os/Build.java
@@ -18,7 +18,9 @@
 
 public class Build {
     public static class VERSION_CODES {
+        public static final int CUR_DEVELOPMENT = 10000;
         public static final int DONUT = 4;
         public static final int R = 30;
+        public static final int S = CUR_DEVELOPMENT;
     }
 }
diff --git a/errorprone/tests/res/com/android/internal/telephony/ITelephony.java b/errorprone/tests/res/com/android/internal/telephony/ITelephony.java
new file mode 100644
index 0000000..61c4dd5
--- /dev/null
+++ b/errorprone/tests/res/com/android/internal/telephony/ITelephony.java
@@ -0,0 +1,23 @@
+/*
+ * 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.telephony;
+
+import android.os.RemoteException;
+
+public interface ITelephony extends android.os.IInterface {
+    public void bar() throws RemoteException;
+}
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 8f496d0..f6edc07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -66,10 +66,7 @@
 
     public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool,
             ShellExecutor mainExecutor, ShellExecutor animExecutor) {
-        super();
-        addListener(new FullscreenTaskListener(syncQueue), WINDOWING_MODE_FULLSCREEN);
-        mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor);
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions);
+        this(null, syncQueue, transactionPool, mainExecutor, animExecutor);
     }
 
     @VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 7c26251..488f909 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -18,7 +18,10 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
 import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
 import android.media.session.MediaController;
 
 import com.android.wm.shell.pip.phone.PipTouchHandler;
@@ -232,4 +235,28 @@
      */
     default void suspendPipResizing(int reason) {
     }
+
+    /**
+     * Called by Launcher when swiping an auto-pip enabled Activity to home starts
+     * @param componentName {@link ComponentName} represents the Activity entering PiP
+     * @param activityInfo {@link ActivityInfo} tied to the Activity
+     * @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity
+     * @param launcherRotation Rotation Launcher is in
+     * @param shelfHeight Shelf height when landing PiP window onto Launcher
+     * @return Destination bounds of PiP window based on the parameters passed in
+     */
+    default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams,
+            int launcherRotation, int shelfHeight) {
+        return null;
+    }
+
+    /**
+     * Called by Launcher when swiping an auto-pip enable Activity to home finishes
+     * @param componentName {@link ComponentName} represents the Activity entering PiP
+     * @param destinationBounds Destination bounds of PiP window
+     */
+    default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        return;
+    }
 }
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 bb501fb..05877d4 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
@@ -58,6 +58,7 @@
 import android.window.WindowContainerTransaction;
 import android.window.WindowContainerTransactionCallback;
 
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.os.SomeArgs;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -88,7 +89,7 @@
  * This class is also responsible for general resize/offset PiP operations within SysUI component,
  * see also {@link PipMotionHelper}.
  */
-public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganizer.TaskListener,
+public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
         DisplayController.OnDisplaysChangedListener {
     private static final String TAG = PipTaskOrganizer.class.getSimpleName();
     private static final boolean DEBUG = false;
@@ -104,7 +105,8 @@
         UNDEFINED(0),
         TASK_APPEARED(1),
         ENTERING_PIP(2),
-        EXITING_PIP(3);
+        ENTERED_PIP(3),
+        EXITING_PIP(4);
 
         private final int mStateValue;
 
@@ -149,15 +151,25 @@
             new PipAnimationController.PipAnimationCallback() {
         @Override
         public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
-            sendOnPipTransitionStarted(animator.getTransitionDirection());
+            final int direction = animator.getTransitionDirection();
+            if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                InteractionJankMonitor.getInstance().begin(
+                        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP, 2000);
+            }
+            sendOnPipTransitionStarted(direction);
         }
 
         @Override
         public void onPipAnimationEnd(SurfaceControl.Transaction tx,
                 PipAnimationController.PipTransitionAnimator animator) {
-            finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(),
+            final int direction = animator.getTransitionDirection();
+            finishResize(tx, animator.getDestinationBounds(), direction,
                     animator.getAnimationType());
-            sendOnPipTransitionFinished(animator.getTransitionDirection());
+            sendOnPipTransitionFinished(direction);
+            if (direction == TRANSITION_DIRECTION_TO_PIP) {
+                InteractionJankMonitor.getInstance().end(
+                        InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
+            }
         }
 
         @Override
@@ -241,6 +253,14 @@
      */
     private boolean mShouldDeferEnteringPip;
 
+    /**
+     * If set to {@code true}, no entering PiP transition would be kicked off and most likely
+     * it's due to the fact that Launcher is handling the transition directly when swiping
+     * auto PiP-able Activity to home.
+     * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}.
+     */
+    private boolean mShouldIgnoreEnteringPipTransition;
+
     public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional,
@@ -309,6 +329,27 @@
     }
 
     /**
+     * Callback when Launcher starts swipe-pip-to-home operation.
+     * @return {@link Rect} for destination bounds.
+     */
+    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams) {
+        mShouldIgnoreEnteringPipTransition = true;
+        mState = State.ENTERING_PIP;
+        return mPipBoundsHandler.getDestinationBounds(componentName,
+                getAspectRatioOrDefault(pictureInPictureParams),
+                null /* bounds */, getMinimalSize(activityInfo));
+    }
+
+    /**
+     * Callback when launcher finishes swipe-pip-to-home operation.
+     * Expect {@link #onTaskAppeared(ActivityManager.RunningTaskInfo, SurfaceControl)} afterwards.
+     */
+    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        mLastReportedBounds.set(destinationBounds);
+    }
+
+    /**
      * Expands PiP to the previous bounds, this is done in two phases using
      * {@link WindowContainerTransaction}
      * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the
@@ -415,7 +456,7 @@
             wct.setBounds(mToken, null);
             mTaskOrganizer.applyTransaction(wct);
 
-            ActivityTaskManager.getService().removeStacksInWindowingModes(
+            ActivityTaskManager.getService().removeRootTasksInWindowingModes(
                     new int[]{ WINDOWING_MODE_PINNED });
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to remove PiP", e);
@@ -435,6 +476,16 @@
         mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
         mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
 
+        if (mShouldIgnoreEnteringPipTransition) {
+            // Animation has been finished together with Recents, directly apply the sync
+            // transaction to PiP here.
+            applyEnterPipSyncTransaction(mLastReportedBounds, () -> {
+                mState = State.ENTERED_PIP;
+            });
+            mShouldIgnoreEnteringPipTransition = false;
+            return;
+        }
+
         if (mShouldDeferEnteringPip) {
             if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
             // if deferred, hide the surface till fixed rotation is completed
@@ -489,6 +540,20 @@
                 mSurfaceControlTransactionFactory.getTransaction();
         tx.setAlpha(mLeash, 0f);
         tx.apply();
+        applyEnterPipSyncTransaction(destinationBounds, () -> {
+            mUpdateHandler.post(() -> mPipAnimationController
+                    .getAnimator(mLeash, destinationBounds, 0f, 1f)
+                    .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+                    .setPipAnimationCallback(mPipAnimationCallback)
+                    .setDuration(durationMs)
+                    .start());
+            // mState is set right after the animation is kicked off to block any resize
+            // requests such as offsetPip that may have been called prior to the transition.
+            mState = State.ENTERING_PIP;
+        });
+    }
+
+    private void applyEnterPipSyncTransaction(Rect destinationBounds, Runnable runnable) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
         wct.setBounds(mToken, destinationBounds);
@@ -497,15 +562,9 @@
             @Override
             public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                 t.apply();
-                mUpdateHandler.post(() -> mPipAnimationController
-                        .getAnimator(mLeash, destinationBounds, 0f, 1f)
-                        .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
-                        .setPipAnimationCallback(mPipAnimationCallback)
-                        .setDuration(durationMs)
-                        .start());
-                // mState is set right after the animation is kicked off to block any resize
-                // requests such as offsetPip that may have been called prior to the transition.
-                mState = State.ENTERING_PIP;
+                if (runnable != null) {
+                    runnable.run();
+                }
             }
         });
     }
@@ -523,6 +582,9 @@
 
     private void sendOnPipTransitionFinished(
             @PipAnimationController.TransitionDirection int direction) {
+        if (direction == TRANSITION_DIRECTION_TO_PIP) {
+            mState = State.ENTERED_PIP;
+        }
         runOnMainHandler(() -> {
             for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
                 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
@@ -606,7 +668,7 @@
     /**
      * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
-     *   - as a result of removeStacksInWindowingModes from WM
+     *   - as a result of removeRootTasksInWindowingModes from WM
      *   - activity itself is died
      * Nevertheless, we simply update the internal state here as all the heavy lifting should
      * have been done in WM.
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 5193656..41c0a88 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
@@ -22,9 +22,11 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.PictureInPictureParams;
 import android.app.RemoteAction;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -87,7 +89,7 @@
         }
         // If there is an animation running (ie. from a shelf offset), then ensure that we calculate
         // the bounds for the next orientation using the destination bounds of the animation
-        // TODO: Techincally this should account for movement animation bounds as well
+        // TODO: Technically this should account for movement animation bounds as well
         Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds();
         final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext,
                 mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation,
@@ -351,16 +353,18 @@
      */
     @Override
     public void setShelfHeight(boolean visible, int height) {
-        mHandler.post(() -> {
-            final int shelfHeight = visible ? height : 0;
-            final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
-            if (changed) {
-                mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
-                updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
-                        false /* fromRotation */, false /* fromImeAdjustment */,
-                        true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
-            }
-        });
+        mHandler.post(() -> setShelfHeightLocked(visible, height));
+    }
+
+    private void setShelfHeightLocked(boolean visible, int height) {
+        final int shelfHeight = visible ? height : 0;
+        final boolean changed = mPipBoundsHandler.setShelfHeight(visible, shelfHeight);
+        if (changed) {
+            mTouchHandler.onShelfVisibilityChanged(visible, shelfHeight);
+            updateMovementBounds(mPipTaskOrganizer.getLastReportedBounds(),
+                    false /* fromRotation */, false /* fromImeAdjustment */,
+                    true /* fromShelfAdjustment */, null /* windowContainerTransaction */);
+        }
     }
 
     @Override
@@ -374,6 +378,21 @@
     }
 
     @Override
+    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+            PictureInPictureParams pictureInPictureParams,
+            int launcherRotation, int shelfHeight) {
+        setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight);
+        mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, launcherRotation);
+        return mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo,
+                pictureInPictureParams);
+    }
+
+    @Override
+    public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+        mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds);
+    }
+
+    @Override
     public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
         if (isOutPipDirection(direction)) {
             // Exiting PIP, save the reentry bounds to restore to when re-entering.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
new file mode 100644
index 0000000..bebe5f9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -0,0 +1,295 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.TransitionDrawable;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.common.DismissCircleView;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.pip.PipUiEventLogger;
+
+import kotlin.Unit;
+
+/**
+ * Handler of all Magnetized Object related code for PiP.
+ */
+public class PipDismissTargetHandler {
+
+    /* The multiplier to apply scale the target size by when applying the magnetic field radius */
+    private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
+
+    /** Duration of the dismiss scrim fading in/out. */
+    private static final int DISMISS_TRANSITION_DURATION_MS = 200;
+
+    /**
+     * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
+     * PIP.
+     */
+    private final MagnetizedObject<Rect> mMagnetizedPip;
+
+    /**
+     * Container for the dismiss circle, so that it can be animated within the container via
+     * translation rather than within the WindowManager via slow layout animations.
+     */
+    private final ViewGroup mTargetViewContainer;
+
+    /** Circle view used to render the dismiss target. */
+    private final DismissCircleView mTargetView;
+
+    /**
+     * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
+     */
+    private final MagnetizedObject.MagneticTarget mMagneticTarget;
+
+    /** PhysicsAnimator instance for animating the dismiss target in/out. */
+    private final PhysicsAnimator<View> mMagneticTargetAnimator;
+
+    /** Default configuration to use for springing the dismiss target in/out. */
+    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
+            new PhysicsAnimator.SpringConfig(
+                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+
+    /**
+     * Runnable that can be posted delayed to show the target. This needs to be saved as a member
+     * variable so we can pass it to removeCallbacks.
+     */
+    private Runnable mShowTargetAction = this::showDismissTargetMaybe;
+
+    // Allow dragging the PIP to a location to close it
+    private final boolean mEnableDismissDragToEdge;
+
+    private int mDismissAreaHeight;
+
+    private final Context mContext;
+    private final PipMotionHelper mMotionHelper;
+    private final PipUiEventLogger mPipUiEventLogger;
+    private final WindowManager mWindowManager;
+    private final Handler mHandler;
+
+    public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
+            PipMotionHelper motionHelper, Handler handler) {
+        mContext = context;
+        mPipUiEventLogger = pipUiEventLogger;
+        mMotionHelper = motionHelper;
+        mHandler = handler;
+        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+
+        Resources res = context.getResources();
+        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
+        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+
+        mTargetView = new DismissCircleView(context);
+        mTargetViewContainer = new FrameLayout(context);
+        mTargetViewContainer.setBackgroundDrawable(
+                context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
+        mTargetViewContainer.setClipChildren(false);
+        mTargetViewContainer.addView(mTargetView);
+
+        mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+        mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
+        updateMagneticTargetSize();
+
+        mMagnetizedPip.setAnimateStuckToTarget(
+                (target, velX, velY, flung, after) -> {
+                    if (mEnableDismissDragToEdge) {
+                        mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
+                    }
+                    return Unit.INSTANCE;
+                });
+        mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
+            @Override
+            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                // Show the dismiss target, in case the initial touch event occurred within the
+                // magnetic field radius.
+                if (mEnableDismissDragToEdge) {
+                    showDismissTargetMaybe();
+                }
+            }
+
+            @Override
+            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+                    float velX, float velY, boolean wasFlungOut) {
+                if (wasFlungOut) {
+                    mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
+                    hideDismissTargetMaybe();
+                } else {
+                    mMotionHelper.setSpringingToTouch(true);
+                }
+            }
+
+            @Override
+            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+                mMotionHelper.notifyDismissalPending();
+
+                handler.post(() -> {
+                    mMotionHelper.animateDismiss();
+                    hideDismissTargetMaybe();
+                });
+
+                mPipUiEventLogger.log(
+                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
+            }
+        });
+
+        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
+    }
+
+    /**
+     * Potentially start consuming future motion events if PiP is currently near the magnetized
+     * object.
+     */
+    public boolean maybeConsumeMotionEvent(MotionEvent ev) {
+        return mMagnetizedPip.maybeConsumeMotionEvent(ev);
+    }
+
+    /**
+     * Update the magnet size.
+     */
+    public void updateMagneticTargetSize() {
+        if (mTargetView == null) {
+            return;
+        }
+
+        final Resources res = mContext.getResources();
+        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
+        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
+        final FrameLayout.LayoutParams newParams =
+                new FrameLayout.LayoutParams(targetSize, targetSize);
+        newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+        newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
+                R.dimen.floating_dismiss_bottom_margin);
+        mTargetView.setLayoutParams(newParams);
+
+        // Set the magnetic field radius equal to the target size from the center of the target
+        mMagneticTarget.setMagneticFieldRadiusPx(
+                (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
+    }
+
+    /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
+    public void createOrUpdateDismissTarget() {
+        if (!mTargetViewContainer.isAttachedToWindow()) {
+            mHandler.removeCallbacks(mShowTargetAction);
+            mMagneticTargetAnimator.cancel();
+
+            mTargetViewContainer.setVisibility(View.INVISIBLE);
+
+            try {
+                mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
+            } catch (IllegalStateException e) {
+                // This shouldn't happen, but if the target is already added, just update its layout
+                // params.
+                mWindowManager.updateViewLayout(
+                        mTargetViewContainer, getDismissTargetLayoutParams());
+            }
+        } else {
+            mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams());
+        }
+    }
+
+    /** Returns layout params for the dismiss target, using the latest display metrics. */
+    private WindowManager.LayoutParams getDismissTargetLayoutParams() {
+        final Point windowSize = new Point();
+        mWindowManager.getDefaultDisplay().getRealSize(windowSize);
+
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.MATCH_PARENT,
+                mDismissAreaHeight,
+                0, windowSize.y - mDismissAreaHeight,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSLUCENT);
+
+        lp.setTitle("pip-dismiss-overlay");
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        lp.setFitInsetsTypes(0 /* types */);
+
+        return lp;
+    }
+
+    /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
+    public void showDismissTargetMaybe() {
+        if (!mEnableDismissDragToEdge) {
+            return;
+        }
+
+        createOrUpdateDismissTarget();
+
+        if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
+
+            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
+            mTargetViewContainer.setVisibility(View.VISIBLE);
+
+            // Cancel in case we were in the middle of animating it out.
+            mMagneticTargetAnimator.cancel();
+            mMagneticTargetAnimator
+                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
+                    .start();
+
+            ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
+                    DISMISS_TRANSITION_DURATION_MS);
+        }
+    }
+
+    /** Animates the magnetic dismiss target out and then sets it to GONE. */
+    public void hideDismissTargetMaybe() {
+        if (!mEnableDismissDragToEdge) {
+            return;
+        }
+
+        mHandler.removeCallbacks(mShowTargetAction);
+        mMagneticTargetAnimator
+                .spring(DynamicAnimation.TRANSLATION_Y,
+                        mTargetViewContainer.getHeight(),
+                        mTargetSpringConfig)
+                .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
+                .start();
+
+        ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
+                DISMISS_TRANSITION_DURATION_MS);
+    }
+
+    /**
+     * Removes the dismiss target and cancels any pending callbacks to show it.
+     */
+    public void cleanUpDismissTarget() {
+        mHandler.removeCallbacks(mShowTargetAction);
+
+        if (mTargetViewContainer.isAttachedToWindow()) {
+            mWindowManager.removeViewImmediate(mTargetViewContainer);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
index c53803a7..cd47d55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
@@ -156,20 +156,6 @@
     }
 
     /**
-     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
-     */
-    public void setDismissFraction(float fraction) {
-        final boolean isMenuVisible = isMenuVisible();
-        if (DEBUG) {
-            Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
-                    + " fraction=" + fraction);
-        }
-        if (isMenuVisible) {
-            mPipMenuView.updateDismissFraction(fraction);
-        }
-    }
-
-    /**
      * Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu
      * upon PiP window transition is finished.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 24e49f8..5195140 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -429,25 +429,6 @@
         }
     }
 
-    void updateDismissFraction(float fraction) {
-        int alpha;
-        final float menuAlpha = 1 - fraction;
-        if (mMenuState == MENU_STATE_FULL) {
-            mMenuContainer.setAlpha(menuAlpha);
-            mSettingsButton.setAlpha(menuAlpha);
-            mDismissButton.setAlpha(menuAlpha);
-            final float interpolatedAlpha =
-                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
-            alpha = (int) (interpolatedAlpha * 255);
-        } else {
-            if (mMenuState == MENU_STATE_CLOSE) {
-                mDismissButton.setAlpha(menuAlpha);
-            }
-            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
-        }
-        mBackgroundDrawable.setAlpha(alpha);
-    }
-
     private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
         mMenuState = menuState;
         mController.onMenuStateChanged(menuState, resize, callback);
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 cc86cf9..fe1d44c7 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
@@ -385,23 +385,20 @@
      * Flings the PiP to the closest snap target.
      */
     void flingToSnapTarget(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction) {
-        movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */);
+            float velocityX, float velocityY, @Nullable Runnable endAction) {
+        movetoTarget(velocityX, velocityY, endAction, false /* isStash */);
     }
 
     /**
      * Stash PiP to the closest edge.
      */
     void stashToEdge(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction) {
-        movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */);
+            float velocityX, float velocityY, @Nullable Runnable endAction) {
+        movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
     }
 
     private void movetoTarget(
-            float velocityX, float velocityY,
-            @Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) {
+            float velocityX, float velocityY, @Nullable Runnable endAction, boolean isStash) {
         // If we're flinging to a snap target now, we're not springing to catch up to the touch
         // location now.
         mSpringingToTouch = false;
@@ -416,11 +413,6 @@
                         FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
                 .withEndActions(endAction);
 
-        if (updateAction != null) {
-            mTemporaryBoundsPhysicsAnimator.addUpdateListener(
-                    (target, values) -> updateAction.run());
-        }
-
         final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
         final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left;
         final float rightEdge = isStash ?  mMovementBounds.right + offset : mMovementBounds.right;
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 6b31772..07beb43 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
@@ -26,40 +26,26 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
 import android.util.Log;
 import android.util.Size;
-import android.view.Gravity;
 import android.view.IPinnedStackController;
 import android.view.InputEvent;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
-import android.widget.FrameLayout;
-
-import androidx.annotation.NonNull;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.R;
-import com.android.wm.shell.animation.PhysicsAnimator;
-import com.android.wm.shell.common.DismissCircleView;
 import com.android.wm.shell.common.FloatingContentCoordinator;
-import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
 import com.android.wm.shell.pip.PipAnimationController;
 import com.android.wm.shell.pip.PipBoundsHandler;
 import com.android.wm.shell.pip.PipTaskOrganizer;
@@ -67,8 +53,6 @@
 
 import java.io.PrintWriter;
 
-import kotlin.Unit;
-
 /**
  * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
  * the PIP.
@@ -82,14 +66,12 @@
     /* The multiplier to apply scale the target size by when applying the magnetic field radius */
     private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
 
-    // Allow dragging the PIP to a location to close it
-    private final boolean mEnableDismissDragToEdge;
     // Allow PIP to resize to a slightly bigger state upon touch
     private final boolean mEnableResize;
     private final Context mContext;
-    private final WindowManager mWindowManager;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipUiEventLogger mPipUiEventLogger;
+    private final PipDismissTargetHandler mPipDismissTargetHandler;
 
     private PipResizeGestureHandler mPipResizeGestureHandler;
     private IPinnedStackController mPinnedStackController;
@@ -105,34 +87,6 @@
      */
     private boolean mEnableStash = false;
 
-    /**
-     * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
-     * PIP.
-     */
-    private MagnetizedObject<Rect> mMagnetizedPip;
-
-    /**
-     * Container for the dismiss circle, so that it can be animated within the container via
-     * translation rather than within the WindowManager via slow layout animations.
-     */
-    private ViewGroup mTargetViewContainer;
-
-    /** Circle view used to render the dismiss target. */
-    private DismissCircleView mTargetView;
-
-    /**
-     * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
-     */
-    private MagnetizedObject.MagneticTarget mMagneticTarget;
-
-    /** PhysicsAnimator instance for animating the dismiss target in/out. */
-    private PhysicsAnimator<View> mMagneticTargetAnimator;
-
-    /** Default configuration to use for springing the dismiss target in/out. */
-    private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
-            new PhysicsAnimator.SpringConfig(
-                    SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
-
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
 
@@ -150,12 +104,6 @@
     private int mDeferResizeToNormalBoundsUntilRotation = -1;
     private int mDisplayRotation;
 
-    /**
-     * Runnable that can be posted delayed to show the target. This needs to be saved as a member
-     * variable so we can pass it to removeCallbacks.
-     */
-    private Runnable mShowTargetAction = this::showDismissTargetMaybe;
-
     private Handler mHandler = new Handler();
 
     // Behaviour states
@@ -163,7 +111,6 @@
     private boolean mIsImeShowing;
     private int mImeHeight;
     private int mImeOffset;
-    private int mDismissAreaHeight;
     private boolean mIsShelfShowing;
     private int mShelfHeight;
     private int mMovementBoundsExtraOffsets;
@@ -221,7 +168,6 @@
         mContext = context;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsHandler = pipBoundsHandler;
-        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mMenuController = menuController;
         mMenuController.addListener(new PipMenuListener());
         mGesture = new DefaultPipTouchGesture();
@@ -231,13 +177,14 @@
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         pipTaskOrganizer, this::getMovementBounds,
                         this::updateMovementBounds, pipUiEventLogger, menuController);
+        mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
+                mMotionHelper, mHandler);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
                 menuController::hideMenu);
 
         Resources res = context.getResources();
-        mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
         mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
         reloadResources();
 
@@ -248,61 +195,6 @@
 
         mPipUiEventLogger = pipUiEventLogger;
 
-        mTargetView = new DismissCircleView(context);
-        mTargetViewContainer = new FrameLayout(context);
-        mTargetViewContainer.setBackgroundDrawable(
-                context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
-        mTargetViewContainer.setClipChildren(false);
-        mTargetViewContainer.addView(mTargetView);
-
-        mMagnetizedPip = mMotionHelper.getMagnetizedPip();
-        mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
-        updateMagneticTargetSize();
-
-        mMagnetizedPip.setAnimateStuckToTarget(
-                (target, velX, velY, flung, after) -> {
-                    if (mEnableDismissDragToEdge) {
-                        mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
-                    }
-                    return Unit.INSTANCE;
-                });
-        mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
-            @Override
-            public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                // Show the dismiss target, in case the initial touch event occurred within the
-                // magnetic field radius.
-                if (mEnableDismissDragToEdge) {
-                    showDismissTargetMaybe();
-                }
-            }
-
-            @Override
-            public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
-                    float velX, float velY, boolean wasFlungOut) {
-                if (wasFlungOut) {
-                    mMotionHelper.flingToSnapTarget(velX, velY, null, null);
-                    hideDismissTarget();
-                } else {
-                    mMotionHelper.setSpringingToTouch(true);
-                }
-            }
-
-            @Override
-            public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
-                mMotionHelper.notifyDismissalPending();
-
-                mHandler.post(() -> {
-                    mMotionHelper.animateDismiss();
-                    hideDismissTarget();
-                });
-
-                mPipUiEventLogger.log(
-                        PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
-            }
-        });
-
-        mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
-
         mEnableStash = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 PIP_STASHING,
@@ -323,27 +215,7 @@
         mExpandedShortestEdgeSize = res.getDimensionPixelSize(
                 R.dimen.pip_expanded_shortest_edge_size);
         mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
-        mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
-        updateMagneticTargetSize();
-    }
-
-    private void updateMagneticTargetSize() {
-        if (mTargetView == null) {
-            return;
-        }
-
-        final Resources res = mContext.getResources();
-        final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
-        final FrameLayout.LayoutParams newParams =
-                new FrameLayout.LayoutParams(targetSize, targetSize);
-        newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-        newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
-                R.dimen.floating_dismiss_bottom_margin);
-        mTargetView.setLayoutParams(newParams);
-
-        // Set the magnetic field radius equal to the target size from the center of the target
-        mMagneticTarget.setMagneticFieldRadiusPx(
-                (int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
+        mPipDismissTargetHandler.updateMagneticTargetSize();
     }
 
     private boolean shouldShowResizeHandle() {
@@ -368,7 +240,7 @@
     }
 
     public void onActivityPinned() {
-        createOrUpdateDismissTarget();
+        mPipDismissTargetHandler.createOrUpdateDismissTarget();
 
         mShowPipMenuOnAnimationEnd = true;
         mPipResizeGestureHandler.onActivityPinned();
@@ -378,7 +250,7 @@
     public void onActivityUnpinned(ComponentName topPipActivity) {
         if (topPipActivity == null) {
             // Clean up state after the last PiP activity is removed
-            cleanUpDismissTarget();
+            mPipDismissTargetHandler.cleanUpDismissTarget();
 
             mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
         }
@@ -409,7 +281,7 @@
         reloadResources();
 
         // Recreate the dismiss target for the new orientation.
-        createOrUpdateDismissTarget();
+        mPipDismissTargetHandler.createOrUpdateDismissTarget();
     }
 
     public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
@@ -553,94 +425,6 @@
         }
     }
 
-    /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
-    private void createOrUpdateDismissTarget() {
-        if (!mTargetViewContainer.isAttachedToWindow()) {
-            mHandler.removeCallbacks(mShowTargetAction);
-            mMagneticTargetAnimator.cancel();
-
-            mTargetViewContainer.setVisibility(View.INVISIBLE);
-
-            try {
-                mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
-            } catch (IllegalStateException e) {
-                // This shouldn't happen, but if the target is already added, just update its layout
-                // params.
-                mWindowManager.updateViewLayout(
-                        mTargetViewContainer, getDismissTargetLayoutParams());
-            }
-        } else {
-            mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams());
-        }
-    }
-
-    /** Returns layout params for the dismiss target, using the latest display metrics. */
-    private WindowManager.LayoutParams getDismissTargetLayoutParams() {
-        final Point windowSize = new Point();
-        mWindowManager.getDefaultDisplay().getRealSize(windowSize);
-
-        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.MATCH_PARENT,
-                mDismissAreaHeight,
-                0, windowSize.y - mDismissAreaHeight,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                PixelFormat.TRANSLUCENT);
-
-        lp.setTitle("pip-dismiss-overlay");
-        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
-        lp.setFitInsetsTypes(0 /* types */);
-
-        return lp;
-    }
-
-    /** Makes the dismiss target visible and animates it in, if it isn't already visible. */
-    private void showDismissTargetMaybe() {
-        createOrUpdateDismissTarget();
-
-        if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-
-            mTargetView.setTranslationY(mTargetViewContainer.getHeight());
-            mTargetViewContainer.setVisibility(View.VISIBLE);
-
-            // Cancel in case we were in the middle of animating it out.
-            mMagneticTargetAnimator.cancel();
-            mMagneticTargetAnimator
-                    .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
-                    .start();
-
-            ((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
-                    DISMISS_TRANSITION_DURATION_MS);
-        }
-    }
-
-    /** Animates the magnetic dismiss target out and then sets it to GONE. */
-    private void hideDismissTarget() {
-        mHandler.removeCallbacks(mShowTargetAction);
-        mMagneticTargetAnimator
-                .spring(DynamicAnimation.TRANSLATION_Y,
-                        mTargetViewContainer.getHeight(),
-                        mTargetSpringConfig)
-                .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
-                .start();
-
-        ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
-                DISMISS_TRANSITION_DURATION_MS);
-    }
-
-    /**
-     * Removes the dismiss target and cancels any pending callbacks to show it.
-     */
-    private void cleanUpDismissTarget() {
-        mHandler.removeCallbacks(mShowTargetAction);
-
-        if (mTargetViewContainer.isAttachedToWindow()) {
-            mWindowManager.removeViewImmediate(mTargetViewContainer);
-        }
-    }
-
     /**
      * TODO Add appropriate description
      */
@@ -650,7 +434,7 @@
         if (!isRegistered && mTouchState.isUserInteracting()) {
             // If the input consumer is unregistered while the user is interacting, then we may not
             // get the final TOUCH_UP event, so clean up the dismiss target as well
-            cleanUpDismissTarget();
+            mPipDismissTargetHandler.cleanUpDismissTarget();
         }
     }
 
@@ -683,7 +467,7 @@
         }
 
         if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
-                && mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
+                && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
             // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
             // to the touch state. Touch state needs a DOWN event in order to later process MOVE
             // events it'll receive if the object is dragged out of the magnetic field.
@@ -793,25 +577,6 @@
     }
 
     /**
-     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
-     */
-    private void updateDismissFraction() {
-        if (mMenuController != null) {
-            Rect bounds = mMotionHelper.getBounds();
-            final float target = mInsetBounds.bottom;
-            float fraction = 0f;
-            if (bounds.bottom > target) {
-                final float distance = bounds.bottom - target;
-                fraction = Math.min(distance / bounds.height(), 1f);
-            }
-            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
-                // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
-                mMenuController.setDismissFraction(fraction);
-            }
-        }
-    }
-
-    /**
      * Sets the controller to update the system of changes from user interaction.
      */
     void setPinnedStackController(IPinnedStackController controller) {
@@ -958,13 +723,7 @@
 
             if (touchState.startedDragging()) {
                 mSavedSnapFraction = -1f;
-
-                if (mEnableDismissDragToEdge) {
-                    if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-                        mHandler.removeCallbacks(mShowTargetAction);
-                        showDismissTargetMaybe();
-                    }
-                }
+                mPipDismissTargetHandler.showDismissTargetMaybe();
             }
 
             if (touchState.isDragging()) {
@@ -995,9 +754,7 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
-            if (mEnableDismissDragToEdge) {
-                hideDismissTarget();
-            }
+            mPipDismissTargetHandler.hideDismissTargetMaybe();
 
             if (!touchState.isUserInteracting()) {
                 return false;
@@ -1023,12 +780,9 @@
                 if (mEnableStash
                         && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right
                         || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) {
-                    mMotionHelper.stashToEdge(vel.x, vel.y,
-                            PipTouchHandler.this::updateDismissFraction /* updateAction */,
-                            this::flingEndAction /* endAction */);
+                    mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */);
                 } else {
                     mMotionHelper.flingToSnapTarget(vel.x, vel.y,
-                            PipTouchHandler.this::updateDismissFraction /* updateAction */,
                             this::flingEndAction /* endAction */);
                 }
             } else if (mTouchState.isDoubleTap()) {
@@ -1122,7 +876,6 @@
         pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
         pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
         pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
-        pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
         pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
         mPipBoundsHandler.dump(pw, innerPrefix);
         mTouchState.dump(pw, innerPrefix);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 3eec20f..8eac005 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -315,9 +315,9 @@
         mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
         if (removePipStack) {
             try {
-                mActivityTaskManager.removeStack(mPinnedStackId);
+                mActivityTaskManager.removeTask(mPinnedStackId);
             } catch (RemoteException e) {
-                Log.e(TAG, "removeStack failed", e);
+                Log.e(TAG, "removeTask failed", e);
             } finally {
                 mPinnedStackId = INVALID_STACK_ID;
             }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ff00409..f87988c 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -89,6 +89,16 @@
 public class LocationManager {
 
     /**
+     * For apps targeting Android S and above, LocationRequest system APIs may not be used with
+     * PendingIntent location requests.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;
+
+    /**
      * For apps targeting Android S and above, location clients may receive historical locations
      * (from before the present time) under some circumstances.
      *
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 3bb4781..e03643c 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -250,7 +250,7 @@
             boolean hiddenFromAppOps,
             boolean locationSettingsIgnored,
             boolean lowPower,
-            @Nullable WorkSource workSource) {
+            WorkSource workSource) {
         Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE);
         Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis);
 
@@ -265,7 +265,7 @@
         mHideFromAppOps = hiddenFromAppOps;
         mLowPower = lowPower;
         mLocationSettingsIgnored = locationSettingsIgnored;
-        mWorkSource = workSource;
+        mWorkSource = Objects.requireNonNull(workSource);
     }
 
     /**
@@ -645,12 +645,15 @@
     @SystemApi
     @Deprecated
     public void setWorkSource(@Nullable WorkSource workSource) {
+        if (workSource == null) {
+            workSource = new WorkSource();
+        }
         mWorkSource = workSource;
     }
 
     /**
-     * Returns the work source used for power blame for this request. If null, the system is free to
-     * assign power blame as it deems most appropriate.
+     * Returns the work source used for power blame for this request. If empty, the system is free
+     * to assign power blame as it deems most appropriate.
      *
      * @return the work source used for power blame for this request
      *
@@ -658,7 +661,7 @@
      */
     @TestApi
     @SystemApi
-    public @Nullable WorkSource getWorkSource() {
+    public @NonNull WorkSource getWorkSource() {
         return mWorkSource;
     }
 
@@ -1062,9 +1065,9 @@
         }
 
         /**
-         * Sets the work source to use for power blame for this location request. Defaults to null,
-         * which implies the system is free to assign power blame as it determines best for this
-         * request (which usually means blaming the owner of the location listener).
+         * Sets the work source to use for power blame for this location request. Defaults to an
+         * empty WorkSource, which implies the system is free to assign power blame as it determines
+         * best for this request (which usually means blaming the owner of the location listener).
          *
          * <p>Permissions enforcement occurs when resulting location request is actually used, not
          * when this method is invoked.
@@ -1108,7 +1111,7 @@
                     mHiddenFromAppOps,
                     mLocationSettingsIgnored,
                     mLowPower,
-                    mWorkSource);
+                    new WorkSource(mWorkSource));
         }
     }
 }
diff --git a/location/java/android/location/util/identity/CallerIdentity.java b/location/java/android/location/util/identity/CallerIdentity.java
index c32970f..e023aa1 100644
--- a/location/java/android/location/util/identity/CallerIdentity.java
+++ b/location/java/android/location/util/identity/CallerIdentity.java
@@ -150,6 +150,11 @@
         return mListenerId;
     }
 
+    /** Returns true if this represents a system identity. */
+    public boolean isSystem() {
+        return mUid == Process.SYSTEM_UID;
+    }
+
     /**
      * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a
      * new worksource representing this identity.
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index c00ff57..f43eb63 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -59,3 +59,35 @@
 
 }
 
+package com.android.location.timezone.provider {
+
+  public final class LocationTimeZoneEventUnbundled {
+    method public int getEventType();
+    method @NonNull public java.util.List<java.lang.String> getTimeZoneIds();
+    field public static final int EVENT_TYPE_PERMANENT_FAILURE = 1; // 0x1
+    field public static final int EVENT_TYPE_SUCCESS = 2; // 0x2
+    field public static final int EVENT_TYPE_UNCERTAIN = 3; // 0x3
+  }
+
+  public static final class LocationTimeZoneEventUnbundled.Builder {
+    ctor public LocationTimeZoneEventUnbundled.Builder();
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled build();
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setEventType(int);
+    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setTimeZoneIds(@NonNull java.util.List<java.lang.String>);
+  }
+
+  public abstract class LocationTimeZoneProviderBase {
+    ctor public LocationTimeZoneProviderBase(android.content.Context, String);
+    method public final android.os.IBinder getBinder();
+    method protected final android.content.Context getContext();
+    method protected abstract void onSetRequest(@NonNull com.android.location.timezone.provider.LocationTimeZoneProviderRequestUnbundled);
+    method protected final void reportLocationTimeZoneEvent(@NonNull com.android.location.timezone.provider.LocationTimeZoneEventUnbundled);
+  }
+
+  public final class LocationTimeZoneProviderRequestUnbundled {
+    method @IntRange(from=0) public long getInitializationTimeoutMillis();
+    method public boolean getReportLocationTimeZone();
+  }
+
+}
+
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
index 3675574..aa0e895 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.location.timezone.LocationTimeZoneEvent;
 import android.os.SystemClock;
@@ -31,8 +30,6 @@
 /**
  * An event from a {@link LocationTimeZoneProviderBase} sent while determining a device's time zone
  * using its location.
- *
- * @hide
  */
 public final class LocationTimeZoneEventUnbundled {
 
@@ -69,7 +66,6 @@
     /**
      * Returns the event type.
      */
-    @Nullable
     public @EventType int getEventType() {
         return mDelegate.getEventType();
     }
@@ -118,8 +114,6 @@
 
     /**
      * A builder of {@link LocationTimeZoneEventUnbundled} instances.
-     *
-     * @hide
      */
     public static final class Builder {
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
index 9df7166..68ae722 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
@@ -16,6 +16,7 @@
 
 package com.android.location.timezone.provider;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.IBinder;
@@ -55,8 +56,6 @@
  *
  * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
  * API stable.
- *
- * @hide
  */
 public abstract class LocationTimeZoneProviderBase {
 
@@ -85,7 +84,8 @@
     /**
      * Reports a new location time zone event from this provider.
      */
-    protected void reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled event) {
+    protected final void reportLocationTimeZoneEvent(
+            @NonNull LocationTimeZoneEventUnbundled event) {
         ILocationTimeZoneProviderManager manager = mManager;
         if (manager != null) {
             try {
@@ -102,7 +102,7 @@
      * to start returning location time zones, or to stop returning location time zones, depending
      * on the parameters in the request.
      */
-    protected abstract void onSetRequest(LocationTimeZoneProviderRequestUnbundled request);
+    protected abstract void onSetRequest(@NonNull LocationTimeZoneProviderRequestUnbundled request);
 
     private final class Service extends ILocationTimeZoneProvider.Stub {
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index ab50dc3..10d1038 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -16,6 +16,7 @@
 
 package com.android.location.timezone.provider;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 
 import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
@@ -27,8 +28,6 @@
  *
  * <p>IMPORTANT: This class is effectively a public API for unbundled code, and must remain API
  * stable.
- *
- * @hide
  */
 public final class LocationTimeZoneProviderRequestUnbundled {
 
@@ -54,6 +53,7 @@
      * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
      * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
      */
+    @IntRange(from = 0)
     public long getInitializationTimeoutMillis() {
         return mRequest.getInitializationTimeoutMillis();
     }
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java
index 635b130..11cb1f7 100644
--- a/media/java/android/media/tv/TvChannelInfo.java
+++ b/media/java/android/media/tv/TvChannelInfo.java
@@ -22,19 +22,44 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
 
 /**
+ * This class is used to specify information of a TV channel.
  * @hide
  */
 public final class TvChannelInfo implements Parcelable {
     static final String TAG = "TvChannelInfo";
+
+    /**
+     * App tag for {@link #getAppTag()}: the corresponding application of the channel is the same as
+     * the caller.
+     * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is
+     * {@link #APP_TAG_SELF}.
+     */
     public static final int APP_TAG_SELF = 0;
+    /**
+     * App tag for {@link #getAppType()}: the corresponding application of the channel is the same
+     * as the caller.
+     * <p>{@link #getAppType()} returns {@link #APP_TYPE_SELF} if and only if the app tag is
+     * {@link #APP_TAG_SELF}.
+     */
     public static final int APP_TYPE_SELF = 1;
+    /**
+     * App tag for {@link #getAppType()}: the corresponding app of the channel is a system
+     * application.
+     */
     public static final int APP_TYPE_SYSTEM = 2;
+    /**
+     * App tag for {@link #getAppType()}: the corresponding app of the channel is not a system
+     * application.
+     */
     public static final int APP_TYPE_NON_SYSTEM = 3;
 
     /** @hide */
@@ -68,6 +93,7 @@
     @AppType private final int mAppType;
     private final int mAppTag;
 
+    /** @hide */
     public TvChannelInfo(
             String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
             boolean isForeground, @AppType int appType, int appTag) {
@@ -90,24 +116,41 @@
         mAppTag = source.readInt();
     }
 
+    /**
+     * Returns the TV input ID of the channel.
+     */
+    @NonNull
     public String getInputId() {
         return mInputId;
     }
 
+    /**
+     * Returns the channel URI of the channel.
+     * <p>Returns {@code null} if it's a passthrough input or the permission is not granted.
+     */
+    @Nullable
     public Uri getChannelUri() {
         return mChannelUri;
     }
 
+    /**
+     * Returns {@code true} if the channel session is a recording session.
+     * @see TvInputService.RecordingSession
+     */
     public boolean isRecordingSession() {
         return mIsRecordingSession;
     }
 
+    /**
+     * Returns {@code true} if the application is a foreground application.
+     * @see android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+     */
     public boolean isForeground() {
         return mIsForeground;
     }
 
     /**
-     * Gets app tag.
+     * Returns the app tag.
      * <p>App tag is used to differentiate one app from another.
      * {@link #APP_TAG_SELF} is for current app.
      */
@@ -115,6 +158,9 @@
         return mAppTag;
     }
 
+    /**
+     * Returns the app type.
+     */
     @AppType
     public int getAppType() {
         return mAppType;
@@ -126,7 +172,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mInputId);
         String uriString = mChannelUri == null ? null : mChannelUri.toString();
         dest.writeString(uriString);
@@ -145,4 +191,26 @@
                 + ";appType=" + mAppType
                 + ";appTag=" + mAppTag;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof TvChannelInfo)) {
+            return false;
+        }
+
+        TvChannelInfo other = (TvChannelInfo) o;
+
+        return TextUtils.equals(mInputId, other.getInputId())
+                && Objects.equals(mChannelUri, other.mChannelUri)
+                && mIsRecordingSession == other.mIsRecordingSession
+                && mIsForeground == other.mIsForeground
+                && mAppType == other.mAppType
+                && mAppTag == other.mAppTag;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mInputId, mChannelUri, mIsRecordingSession, mIsForeground, mAppType, mAppTag);
+    }
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d38369f..c80f3c6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -900,8 +900,13 @@
         public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
         }
 
-        /** @hide */
-        public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) {
+        /**
+         * This is called when the information about current TV channels has been updated.
+         *
+         * @param tvChannelInfos a list of {@link TvChannelInfo} objects of new current channels.
+         * @hide
+         */
+        public void onCurrentTvChannelInfosUpdated(@NonNull List<TvChannelInfo> tvChannelInfos) {
         }
     }
 
@@ -1976,8 +1981,15 @@
     }
 
     /**
+     * Returns the list of TV channel 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}.
      * @hide
      */
+    @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS")
+    @NonNull
     public List<TvChannelInfo> getCurrentTvChannelInfos() {
         try {
             return mService.getCurrentTvChannelInfos(mUserId);
diff --git a/non-updatable-api/Android.bp b/non-updatable-api/Android.bp
index 4037781..00b9019 100644
--- a/non-updatable-api/Android.bp
+++ b/non-updatable-api/Android.bp
@@ -23,13 +23,31 @@
 }
 
 filegroup {
+    name: "non-updatable-removed.txt",
+    srcs: ["removed.txt"],
+    visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
     name: "non-updatable-system-current.txt",
     srcs: ["system-current.txt"],
     visibility: ["//frameworks/base/api"],
 }
 
 filegroup {
+    name: "non-updatable-system-removed.txt",
+    srcs: ["system-removed.txt"],
+    visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
     name: "non-updatable-module-lib-current.txt",
     srcs: ["module-lib-current.txt"],
     visibility: ["//frameworks/base/api"],
 }
+
+filegroup {
+    name: "non-updatable-module-lib-removed.txt",
+    srcs: ["module-lib-removed.txt"],
+    visibility: ["//frameworks/base/api"],
+}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 983f20a..2bdf1a5 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11454,10 +11454,10 @@
 
   public final class ApkChecksum implements android.os.Parcelable {
     method public int describeContents();
-    method public int getKind();
-    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
-    method @Nullable public String getSourcePackageName();
+    method @Nullable public java.security.cert.Certificate getInstallerCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getInstallerPackageName();
     method @Nullable public String getSplitName();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ApkChecksum> CREATOR;
@@ -11567,17 +11567,17 @@
   public final class Checksum implements android.os.Parcelable {
     ctor public Checksum(int, @NonNull byte[]);
     method public int describeContents();
-    method public int getKind();
+    method public int getType();
     method @NonNull public byte[] getValue();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Checksum> CREATOR;
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
-    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
-    field @Deprecated public static final int WHOLE_MD5 = 2; // 0x2
-    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
-    field @Deprecated public static final int WHOLE_SHA1 = 4; // 0x4
-    field public static final int WHOLE_SHA256 = 8; // 0x8
-    field public static final int WHOLE_SHA512 = 16; // 0x10
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
+    field @Deprecated public static final int TYPE_WHOLE_MD5 = 2; // 0x2
+    field public static final int TYPE_WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field @Deprecated public static final int TYPE_WHOLE_SHA1 = 4; // 0x4
+    field @Deprecated public static final int TYPE_WHOLE_SHA256 = 8; // 0x8
+    field @Deprecated public static final int TYPE_WHOLE_SHA512 = 16; // 0x10
   }
 
   public class ComponentInfo extends android.content.pm.PackageItemInfo {
@@ -12027,7 +12027,6 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
-    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12100,6 +12099,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 1849529..b476e6d 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -126,6 +126,7 @@
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
     field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
+    field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
     field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
     field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
@@ -306,6 +307,7 @@
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemGallery = 17039399; // 0x1040027
+    field public static final int config_systemVideoCall = 17039401; // 0x1040029
   }
 
   public static final class R.style {
@@ -1330,6 +1332,57 @@
 
 }
 
+package android.app.time {
+
+  public final class TimeManager {
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig();
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void removeTimeZoneDetectorListener(@NonNull android.app.time.TimeManager.TimeZoneDetectorListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public boolean updateTimeZoneConfiguration(@NonNull android.app.time.TimeZoneConfiguration);
+  }
+
+  @java.lang.FunctionalInterface public static interface TimeManager.TimeZoneDetectorListener {
+    method public void onChange();
+  }
+
+  public final class TimeZoneCapabilities implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getConfigureAutoDetectionEnabledCapability();
+    method public int getConfigureGeoDetectionEnabledCapability();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14
+    field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e
+    field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa
+    field public static final int CAPABILITY_POSSESSED = 40; // 0x28
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR;
+  }
+
+  public final class TimeZoneCapabilitiesAndConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.app.time.TimeZoneCapabilities getCapabilities();
+    method @NonNull public android.app.time.TimeZoneConfiguration getConfiguration();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilitiesAndConfig> CREATOR;
+  }
+
+  public final class TimeZoneConfiguration implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isAutoDetectionEnabled();
+    method public boolean isGeoDetectionEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneConfiguration> CREATOR;
+  }
+
+  public static final class TimeZoneConfiguration.Builder {
+    ctor public TimeZoneConfiguration.Builder();
+    ctor public TimeZoneConfiguration.Builder(@NonNull android.app.time.TimeZoneConfiguration);
+    method @NonNull public android.app.time.TimeZoneConfiguration build();
+    method @NonNull public android.app.time.TimeZoneConfiguration.Builder setAutoDetectionEnabled(boolean);
+    method @NonNull public android.app.time.TimeZoneConfiguration.Builder setGeoDetectionEnabled(boolean);
+  }
+
+}
+
 package android.app.usage {
 
   public final class CacheQuotaHint implements android.os.Parcelable {
@@ -4058,7 +4111,7 @@
     method @Deprecated @NonNull public String getProvider();
     method public int getQuality();
     method @Deprecated public float getSmallestDisplacement();
-    method @Nullable public android.os.WorkSource getWorkSource();
+    method @NonNull public android.os.WorkSource getWorkSource();
     method public boolean isHiddenFromAppOps();
     method public boolean isLocationSettingsIgnored();
     method public boolean isLowPower();
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9ff8684..f7f3cbb 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -321,6 +321,9 @@
 
         if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
             Log.e(TAG, "Trying to discard AOT while there is no complete installation");
+            // Stop foreground state and dismiss stale notification.
+            stopForeground(STOP_FOREGROUND_REMOVE);
+            resetTaskAndStop();
             return;
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 6c7e03f..f83c7a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -361,6 +361,36 @@
         return null;
     }
 
+    boolean shouldDisableMediaOutput(String packageName) {
+        boolean shouldDisableMediaOutput = false;
+        if (TextUtils.isEmpty(packageName)) {
+            Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
+            return false;
+        }
+        final List<MediaRoute2Info> infos = mRouterManager.getAvailableRoutes(packageName);
+        if (infos.size() == 1) {
+            final MediaRoute2Info info = infos.get(0);
+            final int deviceType = info.getType();
+            switch (deviceType) {
+                case TYPE_UNKNOWN:
+                case TYPE_REMOTE_TV:
+                case TYPE_REMOTE_SPEAKER:
+                case TYPE_GROUP:
+                    shouldDisableMediaOutput = true;
+                    break;
+                default:
+                    shouldDisableMediaOutput = false;
+                    break;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "shouldDisableMediaOutput() MediaRoute2Info size : " + infos.size()
+                    + ", package name : " + packageName + ", shouldDisableMediaOutput : "
+                    + shouldDisableMediaOutput);
+        }
+        return shouldDisableMediaOutput;
+    }
+
     private void refreshDevices() {
         mMediaDevices.clear();
         mCurrentConnectedDevice = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 72a6074..32419f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -402,6 +402,13 @@
         return mPackageName;
     }
 
+    /**
+     * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
+     */
+    public boolean shouldDisableMediaOutput(String packageName) {
+        return mInfoMediaManager.shouldDisableMediaOutput(packageName);
+    }
+
     @VisibleForTesting
     MediaDevice updateCurrentConnectedDevice() {
         MediaDevice connectedDevice = null;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 68f162e..58ca734 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -27,6 +27,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -721,4 +722,47 @@
 
         assertThat(mInfoMediaManager.mMediaDevices.size()).isEqualTo(0);
     }
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeEqual1_returnsTrue() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
+    }
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
+    }
+
+
+    @Test
+    public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
+        final List<MediaRoute2Info> infos = new ArrayList<>();
+        infos.add(info);
+        infos.add(info2);
+        mShadowRouter2Manager.setAvailableRoutes(infos);
+
+        when(mRouterManager.getAvailableRoutes(anyString())).thenReturn(infos);
+        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+        when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+
+        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d2485cc..506b608 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -892,9 +892,6 @@
                 Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                 GlobalSettingsProto.Location.SETTINGS_LINK_TO_PERMISSIONS_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
-                GlobalSettingsProto.Location.GLOBAL_KILL_SWITCH);
-        dumpSetting(s, p,
                 Settings.Global.GNSS_SATELLITE_BLACKLIST,
                 GlobalSettingsProto.Location.GNSS_SATELLITE_BLACKLIST);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 3ccb1f4..710c016 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -328,10 +328,6 @@
         return SettingsState.getUserIdFromKey(key);
     }
 
-    public static String settingTypeToString(int type) {
-        return SettingsState.settingTypeToString(type);
-    }
-
     public static String keyToString(int key) {
         return SettingsState.keyToString(key);
     }
@@ -373,8 +369,7 @@
             }
 
             case Settings.CALL_METHOD_GET_SECURE: {
-                Setting setting = getSecureSetting(name, requestingUserId,
-                        /*enableOverride=*/ true);
+                Setting setting = getSecureSetting(name, requestingUserId);
                 return packageValueForCallResult(setting, isTrackingGeneration(args));
             }
 
@@ -581,7 +576,7 @@
     }
 
     private ArrayList<String> buildSettingsList(Cursor cursor) {
-        final ArrayList<String> lines = new ArrayList<String>();
+        final ArrayList<String> lines = new ArrayList<>();
         try {
             while (cursor != null && cursor.moveToNext()) {
                 lines.add(cursor.getString(1) + "=" + cursor.getString(2));
@@ -1381,10 +1376,6 @@
     }
 
     private Setting getSecureSetting(String name, int requestingUserId) {
-        return getSecureSetting(name, requestingUserId, /*enableOverride=*/ false);
-    }
-
-    private Setting getSecureSetting(String name, int requestingUserId, boolean enableOverride) {
         if (DEBUG) {
             Slog.v(LOG_TAG, "getSecureSetting(" + name + ", " + requestingUserId + ")");
         }
@@ -1414,14 +1405,6 @@
                 return getSsaidSettingLocked(callingPkg, owningUserId);
             }
         }
-        if (enableOverride) {
-            if (Secure.LOCATION_MODE.equals(name)) {
-                final Setting overridden = getLocationModeSetting(owningUserId);
-                if (overridden != null) {
-                    return overridden;
-                }
-            }
-        }
 
         // Not the SSAID; do a straight lookup
         synchronized (mLock) {
@@ -1511,35 +1494,6 @@
         return null;
     }
 
-    private Setting getLocationModeSetting(int owningUserId) {
-        synchronized (mLock) {
-            final Setting setting = getGlobalSetting(
-                    Global.LOCATION_GLOBAL_KILL_SWITCH);
-            if (!"1".equals(setting.getValue())) {
-                return null;
-            }
-            // Global kill-switch is enabled. Return an empty value.
-            final SettingsState settingsState = mSettingsRegistry.getSettingsLocked(
-                    SETTINGS_TYPE_SECURE, owningUserId);
-            return settingsState.new Setting(
-                    Secure.LOCATION_MODE,
-                    "", // value
-                    "", // tag
-                    "", // default value
-                    "", // package name
-                    false, // from system
-                    "0" // id
-            ) {
-                @Override
-                public boolean update(String value, boolean setDefault, String packageName,
-                        String tag, boolean forceNonSystemPackage, boolean overrideableByRestore) {
-                    Slog.wtf(LOG_TAG, "update shouldn't be called on this instance.");
-                    return false;
-                }
-            };
-        }
-    }
-
     private boolean insertSecureSetting(String name, String value, String tag,
             boolean makeDefault, int requestingUserId, boolean forceNotify,
             boolean overrideableByRestore) {
@@ -1808,12 +1762,8 @@
 
     private boolean hasWriteSecureSettingsPermission() {
         // Write secure settings is a more protected permission. If caller has it we are good.
-        if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED) {
-            return true;
-        }
-
-        return false;
+        return getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+                == PackageManager.PERMISSION_GRANTED;
     }
 
     private void validateSystemSettingValue(String name, String value) {
@@ -3174,12 +3124,6 @@
             if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) {
                 final long token = Binder.clearCallingIdentity();
                 try {
-                    if (Global.LOCATION_GLOBAL_KILL_SWITCH.equals(name)
-                            && isGlobalSettingsKey(key)) {
-                        // When the global kill switch is updated, send the
-                        // change notification for the location setting.
-                        notifyLocationChangeForRunningUsers();
-                    }
                     notifySettingChangeForRunningUsers(key, name);
                 } finally {
                     Binder.restoreCallingIdentity(token);
@@ -3257,26 +3201,6 @@
             }
         }
 
-        private void notifyLocationChangeForRunningUsers() {
-            final List<UserInfo> users = mUserManager.getAliveUsers();
-
-            for (int i = 0; i < users.size(); i++) {
-                final int userId = users.get(i).id;
-
-                if (!mUserManager.isUserRunning(UserHandle.of(userId))) {
-                    continue;
-                }
-
-                // Increment the generation first, so observers always see the new value
-                final int key = makeKey(SETTINGS_TYPE_SECURE, userId);
-                mGenerationRegistry.incrementGeneration(key);
-
-                final Uri uri = getNotificationUriFor(key, Secure.LOCATION_MODE);
-                mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED,
-                        userId, 0, uri).sendToTarget();
-            }
-        }
-
         private boolean isConfigSettingsKey(int key) {
             return getTypeFromKey(key) == SETTINGS_TYPE_CONFIG;
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6678cf6..b061df1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -34,7 +34,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -47,7 +46,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
 import libcore.io.IoUtils;
@@ -817,13 +815,6 @@
                 for (int i = 0; i < settingCount; i++) {
                     Setting setting = settings.valueAt(i);
 
-                    if (setting.isTransient()) {
-                        if (DEBUG_PERSISTENCE) {
-                            Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
-                        }
-                        continue;
-                    }
-
                     writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
                             setting.getTag(), setting.isDefaultFromSystem(),
@@ -1109,8 +1100,7 @@
                         ATTR_DEFAULT_VALUE_BASE64);
                 String isPreservedInRestoreString = parser.getAttributeValue(null,
                         ATTR_PRESERVE_IN_RESTORE);
-                boolean isPreservedInRestore = isPreservedInRestoreString != null
-                        && Boolean.parseBoolean(isPreservedInRestoreString);
+                boolean isPreservedInRestore = Boolean.parseBoolean(isPreservedInRestoreString);
                 String tag = null;
                 boolean fromSystem = false;
                 if (defaultValue != null) {
@@ -1308,14 +1298,6 @@
                     /* resetToDefault */ true);
         }
 
-        public boolean isTransient() {
-            switch (getTypeFromKey(getKey())) {
-                case SETTINGS_TYPE_GLOBAL:
-                    return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
-            }
-            return false;
-        }
-
         public boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage, boolean overrideableByRestore) {
             return update(value, setDefault, packageName, tag, forceNonSystemPackage,
@@ -1444,7 +1426,7 @@
     }
 
     private static String fromBytes(byte[] bytes) {
-        final StringBuffer sb = new StringBuffer(bytes.length / 2);
+        final StringBuilder sb = new StringBuilder(bytes.length / 2);
 
         final int last = bytes.length - 1;
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e027fd3..34b7298 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -159,6 +159,7 @@
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_BACKGROUND_MODE,
+                    Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE,
                     Settings.Global.BLOCKED_SLICES,
                     Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT,
                     Settings.Global.BLOCKING_HELPER_STREAK_LIMIT,
@@ -332,6 +333,7 @@
                     Settings.Global.MAX_ERROR_BYTES_PREFIX,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                     Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
+                    Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
                     Settings.Global.MDC_INITIAL_MAX_RETRY,
                     Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
                     Settings.Global.MHL_POWER_CHARGE_ENABLED,
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 649ce392..cb03d40 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</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">"Zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string>
+    <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="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>
@@ -1087,8 +1081,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new 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">"Build number"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 88e497e..8e5849e 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</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">"Zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string>
+    <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="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>
@@ -1087,8 +1081,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new 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">"Build number"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 649ce392..cb03d40 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</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">"Zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string>
+    <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="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>
@@ -1087,8 +1081,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new 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">"Build number"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 649ce392..cb03d40 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Settings"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Magnification window"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Magnification window controls"</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">"Zoom in"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Zoom out"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Move up"</string>
+    <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="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>
@@ -1087,8 +1081,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> (disconnected)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Couldn\'t connect. Try again."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Pair new 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">"Build number"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Build number copied to clipboard."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index f091e0b..e107ed5 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎Settings‎‏‎‎‏‎"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‎‏‏‏‎‏‎Magnification Window‎‏‎‎‏‎"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎Magnification Window Controls‎‏‎‎‏‎"</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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎‎‏‎‎‏‎Zoom in‎‏‎‎‏‎"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎Zoom out‎‏‎‎‏‎"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‎‏‎‎‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‎‎‎Move up‎‏‎‎‏‎"</string>
+    <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="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>
@@ -1087,8 +1081,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‎‏‏‏‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="DEVICE_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ (disconnected)‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‎‎Couldn\'t connect. Try again.‎‏‎‎‏‎"</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‏‏‎‎Pair new 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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‎‏‎‎‎‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎Build number‎‏‎‎‏‎"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‎Build number copied to clipboard.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 524ced3..6fb84dd8 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1022,18 +1022,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Nustatymai"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Didinimo langas"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Didinimo lango valdikliai"</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">"Artinti"</string>
+    <string name="accessibility_control_zoom_out" msgid="69578832020304084">"Tolinti"</string>
+    <string name="accessibility_control_move_up" msgid="6622825494014720136">"Perkelti aukštyn"</string>
+    <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>
     <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>
@@ -1099,8 +1093,6 @@
     <string name="media_output_dialog_disconnected" msgid="1834473104836986046">"„<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“ (atjungta)"</string>
     <string name="media_output_dialog_connect_failed" msgid="3225190634236259010">"Nepavyko prijungti. Bandykite dar kartÄ…."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Naujo įrenginio susiejimas"</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">"Versijos numeris"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Versijos numeris nukopijuotas į iškarpinÄ™."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 46cf45a..b76ebda 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configurações"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</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">"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>
     <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>
@@ -1087,8 +1081,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">"Não foi possível conectar. Tente novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear 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 versão"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versã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 46cf45a..b76ebda 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1012,18 +1012,12 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"Configurações"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"Janela de ampliação"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"Controles da janela de ampliação"</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">"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>
     <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>
@@ -1087,8 +1081,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">"Não foi possível conectar. Tente novamente."</string>
     <string name="media_output_dialog_pairing_new" msgid="9099497976087485862">"Parear 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 versão"</string>
+    <string name="build_number_copy_toast" msgid="877720921605503046">"Número da versão copiado para a área de transferência."</string>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index c94bcaa..e4427f4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -16,6 +16,9 @@
 
 package com.android.systemui.shared.recents;
 
+import android.app.PictureInPictureParams;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -166,4 +169,27 @@
      * Notifies to expand notification panel.
      */
     void expandNotificationPanel() = 29;
+
+    /**
+     * Notifies that Activity is about to be swiped to home with entering PiP transition and
+     * queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
+     *
+     * @param componentName ComponentName represents the Activity
+     * @param activityInfo ActivityInfo tied to the Activity
+     * @param pictureInPictureParams PictureInPictureParams tied to the Activity
+     * @param launcherRotation Launcher rotation to calculate the PiP destination bounds
+     * @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
+     * @return destination bounds the PiP window should land into
+     */
+    Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
+                in PictureInPictureParams pictureInPictureParams,
+                int launcherRotation, int shelfHeight) = 30;
+
+    /**
+     * Notifies the swiping Activity to PiP onto home transition is finished
+     *
+     * @param componentName ComponentName represents the Activity
+     * @param destinationBounds the destination bounds the PiP window lands into
+     */
+    void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31;
 }
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 ee05c6c..b526a92 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
@@ -34,7 +34,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
-import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -43,7 +42,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -228,21 +226,10 @@
     /**
      * Starts the recents activity. The caller should manage the thread on which this is called.
      */
-    public void startRecentsActivity(Intent intent, final AssistDataReceiver assistDataReceiver,
+    public void startRecentsActivity(Intent intent, long eventTime,
             final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
         try {
-            IAssistDataReceiver receiver = null;
-            if (assistDataReceiver != null) {
-                receiver = new IAssistDataReceiver.Stub() {
-                    public void onHandleAssistData(Bundle resultData) {
-                        assistDataReceiver.onHandleAssistData(resultData);
-                    }
-                    public void onHandleAssistScreenshot(Bitmap screenshot) {
-                        assistDataReceiver.onHandleAssistScreenshot(screenshot);
-                    }
-                };
-            }
             IRecentsAnimationRunner runner = null;
             if (animationHandler != null) {
                 runner = new IRecentsAnimationRunner.Stub() {
@@ -272,7 +259,7 @@
                     }
                 };
             }
-            ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner);
+            ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner);
             if (resultCallback != null) {
                 resultCallbackHandler.post(new Runnable() {
                     @Override
@@ -296,9 +283,9 @@
     /**
      * Cancels the remote recents animation started from {@link #startRecentsActivity}.
      */
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
         try {
-            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeStackPosition);
+            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to cancel recents animation", e);
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index e5f2ad5..5122f6c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -81,12 +82,25 @@
             WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
+    public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+
     private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
 
     public static WindowManagerWrapper getInstance() {
         return sInstance;
     }
 
+
+    /**
+     * Sets {@param providesInsetsTypes} as the inset types provided by {@param params}.
+     * @param params The window layout params.
+     * @param providesInsetsTypes The inset types we would like this layout params to provide.
+     */
+    public void setProvidesInsetsTypes(WindowManager.LayoutParams params,
+            int[] providesInsetsTypes) {
+        params.providesInsetsTypes = providesInsetsTypes;
+    }
+
     /**
      * @return the stable insets for the primary display.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 911bf9e..a705ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -37,6 +37,7 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.statusbar.CommandQueue;
 
 import javax.inject.Inject;
@@ -66,7 +67,8 @@
 
     @Inject
     public WindowMagnification(Context context, @Main Handler mainHandler,
-            CommandQueue commandQueue, ModeSwitchesController modeSwitchesController) {
+            CommandQueue commandQueue, ModeSwitchesController modeSwitchesController,
+            NavigationModeController navigationModeController) {
         super(context);
         mHandler = mainHandler;
         mLastConfiguration = new Configuration(context.getResources().getConfiguration());
@@ -77,6 +79,9 @@
         final WindowMagnificationController controller = new WindowMagnificationController(mContext,
                 mHandler, new SfVsyncFrameCallbackProvider(), null,
                 new SurfaceControl.Transaction(), this);
+        final int navBarMode = navigationModeController.addListener(
+                controller::onNavigationModeChanged);
+        controller.onNavigationModeChanged(navBarMode);
         mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
                 mContext, controller);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 3832ff30..c3474bb 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -17,6 +17,8 @@
 package com.android.systemui.accessibility;
 
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -117,6 +119,9 @@
     // The boundary of magnification frame.
     private final Rect mMagnificationFrameBoundary = new Rect();
 
+    private int mNavBarMode;
+    private int mNavGestureHeight;
+
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
     private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
     private Locale mLocale;
@@ -195,6 +200,19 @@
                 R.dimen.magnification_drag_view_size);
         mOuterBorderSize = mResources.getDimensionPixelSize(
                 R.dimen.magnification_outer_border_margin);
+        updateNavigationBarDimensions();
+    }
+
+    private void updateNavigationBarDimensions() {
+        if (!supportsSwipeUpGesture()) {
+            mNavGestureHeight = 0;
+            return;
+        }
+        mNavGestureHeight = (mDisplaySize.x > mDisplaySize.y)
+                ? mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height_landscape)
+                : mResources.getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_gesture_height);
     }
 
     /**
@@ -239,6 +257,13 @@
         }
     }
 
+    /** Handles MirrorWindow position when the navigation bar mode changed. */
+    public void onNavigationModeChanged(int mode) {
+        mNavBarMode = mode;
+        updateNavigationBarDimensions();
+        updateMirrorViewLayout();
+    }
+
     /** Handles MirrorWindow position when the device rotation changed. */
     private void onRotate() {
         final Display display = mContext.getDisplay();
@@ -246,6 +271,7 @@
         display.getRealSize(mDisplaySize);
         setMagnificationFrameBoundary();
         mRotation = display.getRotation();
+        updateNavigationBarDimensions();
 
         if (!isWindowVisible()) {
             return;
@@ -401,15 +427,23 @@
      * moved close to the screen edges.
      */
     private void updateMirrorViewLayout() {
+        if (!isWindowVisible()) {
+            return;
+        }
+        final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth();
+        final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight() - mNavGestureHeight;
         WindowManager.LayoutParams params =
                 (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
+        // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
+        // overlap nav bar window to prevent window-dragging obscured.
+        if (supportsSwipeUpGesture()) {
+            params.y = Math.min(params.y, maxMirrorViewY);
+        }
 
         // Translates MirrorView position to make MirrorSurfaceView that is inside MirrorView
         // able to move close to the screen edges.
-        final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth();
-        final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight();
         final float translationX;
         final float translationY;
         if (params.x < 0) {
@@ -621,6 +655,10 @@
         return mMirrorView != null;
     }
 
+    private boolean supportsSwipeUpGesture() {
+        return mNavBarMode == NAV_BAR_MODE_2BUTTON || mNavBarMode == NAV_BAR_MODE_GESTURAL;
+    }
+
     private CharSequence formatStateDescription(float scale) {
         // Cache the locale-appropriate NumberFormat.  Configuration locale is guaranteed
         // non-null, so the first time this is called we will always get the appropriate
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 5b85208..529af22 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -25,6 +25,7 @@
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.PendingIntent;
+import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -35,16 +36,18 @@
 import android.graphics.Path;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.os.Parcelable;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.InstanceId;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -169,11 +172,10 @@
     }
 
     @VisibleForTesting(visibility = PRIVATE)
-    Bubble(@NonNull final NotificationEntry e,
+    Bubble(@NonNull final BubbleEntry entry,
             @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
             final BubbleController.PendingIntentCanceledListener intentCancelListener) {
-        Objects.requireNonNull(e);
-        mKey = e.getKey();
+        mKey = entry.getKey();
         mSuppressionListener = listener;
         mIntentCancelListener = intent -> {
             if (mIntent != null) {
@@ -181,7 +183,7 @@
             }
             intentCancelListener.onPendingIntentCanceled(this);
         };
-        setEntry(e);
+        setEntry(entry);
     }
 
     @Override
@@ -294,8 +296,8 @@
     }
 
     /**
-     * Sets whether this bubble is considered visually interruptive. Normally pulled from the
-     * {@link NotificationEntry}, this method is purely for testing.
+     * Sets whether this bubble is considered visually interruptive. This method is purely for
+     * testing.
      */
     @VisibleForTesting
     void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) {
@@ -388,30 +390,28 @@
     /**
      * Sets the entry associated with this bubble.
      */
-    void setEntry(@NonNull final NotificationEntry entry) {
+    void setEntry(@NonNull final BubbleEntry entry) {
         Objects.requireNonNull(entry);
-        Objects.requireNonNull(entry.getSbn());
-        mLastUpdated = entry.getSbn().getPostTime();
-        mIsBubble = entry.getSbn().getNotification().isBubbleNotification();
-        mPackageName = entry.getSbn().getPackageName();
-        mUser = entry.getSbn().getUser();
+        mLastUpdated = entry.getStatusBarNotification().getPostTime();
+        mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
+        mPackageName = entry.getStatusBarNotification().getPackageName();
+        mUser = entry.getStatusBarNotification().getUser();
         mTitle = getTitle(entry);
-        mIsClearable = entry.isClearable();
-        mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
-        mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
-        mShouldSuppressPeek = entry.shouldSuppressPeek();
-        mChannelId = entry.getSbn().getNotification().getChannelId();
-        mNotificationId = entry.getSbn().getId();
-        mAppUid = entry.getSbn().getUid();
-        mInstanceId = entry.getSbn().getInstanceId();
-        mFlyoutMessage = BubbleViewInfoTask.extractFlyoutMessage(entry);
-        mShortcutInfo = (entry.getRanking() != null ? entry.getRanking().getShortcutInfo() : null);
-        mMetadataShortcutId = (entry.getBubbleMetadata() != null
-                ? entry.getBubbleMetadata().getShortcutId() : null);
+        mChannelId = entry.getStatusBarNotification().getNotification().getChannelId();
+        mNotificationId = entry.getStatusBarNotification().getId();
+        mAppUid = entry.getStatusBarNotification().getUid();
+        mInstanceId = entry.getStatusBarNotification().getInstanceId();
+        mFlyoutMessage = extractFlyoutMessage(entry);
         if (entry.getRanking() != null) {
+            mShortcutInfo = entry.getRanking().getShortcutInfo();
             mIsVisuallyInterruptive = entry.getRanking().visuallyInterruptive();
+            if (entry.getRanking().getChannel() != null) {
+                mIsImportantConversation =
+                        entry.getRanking().getChannel().isImportantConversation();
+            }
         }
         if (entry.getBubbleMetadata() != null) {
+            mMetadataShortcutId = entry.getBubbleMetadata().getShortcutId();
             mFlags = entry.getBubbleMetadata().getFlags();
             mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
             mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
@@ -433,8 +433,11 @@
             }
             mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
         }
-        mIsImportantConversation =
-                entry.getChannel() != null && entry.getChannel().isImportantConversation();
+
+        mIsClearable = entry.isClearable();
+        mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
+        mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
+        mShouldSuppressPeek = entry.shouldSuppressPeek();
     }
 
     @Nullable
@@ -735,9 +738,75 @@
     }
 
     @Nullable
-    private static String getTitle(@NonNull final NotificationEntry e) {
-        final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence(
-                Notification.EXTRA_TITLE);
+    private static String getTitle(@NonNull final BubbleEntry e) {
+        final CharSequence titleCharSeq = e.getStatusBarNotification()
+                .getNotification().extras.getCharSequence(Notification.EXTRA_TITLE);
         return titleCharSeq == null ? null : titleCharSeq.toString();
     }
+
+    /**
+     * Returns our best guess for the most relevant text summary of the latest update to this
+     * notification, based on its type. Returns null if there should not be an update message.
+     */
+    @NonNull
+    static Bubble.FlyoutMessage extractFlyoutMessage(BubbleEntry entry) {
+        Objects.requireNonNull(entry);
+        final Notification underlyingNotif = entry.getStatusBarNotification().getNotification();
+        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
+
+        Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
+        bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
+                Notification.EXTRA_IS_GROUP_CONVERSATION);
+        try {
+            if (Notification.BigTextStyle.class.equals(style)) {
+                // Return the big text, it is big so probably important. If it's not there use the
+                // normal text.
+                CharSequence bigText =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
+                bubbleMessage.message = !TextUtils.isEmpty(bigText)
+                        ? bigText
+                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            } else if (Notification.MessagingStyle.class.equals(style)) {
+                final List<Notification.MessagingStyle.Message> messages =
+                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
+                                (Parcelable[]) underlyingNotif.extras.get(
+                                        Notification.EXTRA_MESSAGES));
+
+                final Notification.MessagingStyle.Message latestMessage =
+                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
+                if (latestMessage != null) {
+                    bubbleMessage.message = latestMessage.getText();
+                    Person sender = latestMessage.getSenderPerson();
+                    bubbleMessage.senderName = sender != null ? sender.getName() : null;
+                    bubbleMessage.senderAvatar = null;
+                    bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
+                    return bubbleMessage;
+                }
+            } else if (Notification.InboxStyle.class.equals(style)) {
+                CharSequence[] lines =
+                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
+
+                // Return the last line since it should be the most recent.
+                if (lines != null && lines.length > 0) {
+                    bubbleMessage.message = lines[lines.length - 1];
+                    return bubbleMessage;
+                }
+            } else if (Notification.MediaStyle.class.equals(style)) {
+                // Return nothing, media updates aren't typically useful as a text update.
+                return bubbleMessage;
+            } else {
+                // Default to text extra.
+                bubbleMessage.message =
+                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
+                return bubbleMessage;
+            }
+        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
+            // No use crashing, we'll just return null and the caller will assume there's no update
+            // message.
+            e.printStackTrace();
+        }
+
+        return bubbleMessage;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 90b64ea..2372529 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -231,6 +231,11 @@
      */
     private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED;
 
+    /**
+     * Last known font scale, used to detect font size changes in {@link #onConfigChanged}.
+     */
+    private float mFontScale = 0;
+
     /** Last known direction, used to detect layout direction changes @link #onConfigChanged}. */
     private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
 
@@ -345,7 +350,42 @@
     /**
      * Injected constructor. See {@link BubbleModule}.
      */
-    public BubbleController(Context context,
+    public static BubbleController create(Context context,
+            NotificationShadeWindowController notificationShadeWindowController,
+            StatusBarStateController statusBarStateController,
+            ShadeController shadeController,
+            @Nullable BubbleStackView.SurfaceSynchronizer synchronizer,
+            ConfigurationController configurationController,
+            NotificationInterruptStateProvider interruptionStateProvider,
+            ZenModeController zenModeController,
+            NotificationLockscreenUserManager notifUserManager,
+            NotificationGroupManagerLegacy groupManager,
+            NotificationEntryManager entryManager,
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpManager dumpManager,
+            FloatingContentCoordinator floatingContentCoordinator,
+            BubbleDataRepository dataRepository,
+            SysUiState sysUiState,
+            INotificationManager notificationManager,
+            @Nullable IStatusBarService statusBarService,
+            WindowManager windowManager,
+            WindowManagerShellWrapper windowManagerShellWrapper,
+            LauncherApps launcherApps) {
+        return new BubbleController(context, notificationShadeWindowController,
+                statusBarStateController, shadeController, new BubbleData(context), synchronizer,
+                configurationController, interruptionStateProvider, zenModeController,
+                notifUserManager, groupManager, entryManager, notifPipeline, featureFlags,
+                dumpManager, floatingContentCoordinator, dataRepository, sysUiState,
+                notificationManager, statusBarService, windowManager, windowManagerShellWrapper,
+                launcherApps);
+    }
+
+    /**
+     * Testing constructor.
+     */
+    @VisibleForTesting
+    BubbleController(Context context,
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
             ShadeController shadeController,
@@ -955,6 +995,10 @@
                 mBubbleIconFactory = new BubbleIconFactory(mContext);
                 mStackView.onDisplaySizeChanged();
             }
+            if (newConfig.fontScale != mFontScale) {
+                mFontScale = newConfig.fontScale;
+                mStackView.updateFlyout(mFontScale);
+            }
             if (newConfig.getLayoutDirection() != mLayoutDirection) {
                 mLayoutDirection = newConfig.getLayoutDirection();
                 mStackView.onLayoutDirectionChanged(mLayoutDirection);
@@ -1120,9 +1164,10 @@
                 && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
             // Update the bubble but don't promote it out of overflow
             Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
-            b.setEntry(notif);
+            b.setEntry(notifToBubbleEntry(notif));
         } else {
-            Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
+            Bubble bubble = mBubbleData.getOrCreateBubble(
+                    notifToBubbleEntry(notif), null /* persistedBubble */);
             inflateAndAdd(bubble, suppressFlyout, showInShade);
         }
     }
@@ -1208,8 +1253,7 @@
             mBubbleData.removeSuppressedSummary(groupKey);
 
             // Remove any associated bubble children with the summary
-            final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                    groupKey, mNotificationEntryManager);
+            final List<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
             for (int i = 0; i < bubbleChildren.size(); i++) {
                 removeBubble(bubbleChildren.get(i).getKey(), DISMISS_GROUP_CANCELLED);
             }
@@ -1255,6 +1299,25 @@
         }
     }
 
+    /**
+     * Retrieves any bubbles that are part of the notification group represented by the provided
+     * group key.
+     */
+    private ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey) {
+        ArrayList<Bubble> bubbleChildren = new ArrayList<>();
+        if (groupKey == null) {
+            return bubbleChildren;
+        }
+        for (Bubble bubble : mBubbleData.getActiveBubbles()) {
+            final NotificationEntry entry =
+                    mNotificationEntryManager.getPendingOrActiveNotif(bubble.getKey());
+            if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
+                bubbleChildren.add(bubble);
+            }
+        }
+        return bubbleChildren;
+    }
+
     private void setIsBubble(@NonNull final NotificationEntry entry, final boolean isBubble,
             final boolean autoExpand) {
         Objects.requireNonNull(entry);
@@ -1365,8 +1428,7 @@
                 }
                 if (entry != null) {
                     final String groupKey = entry.getSbn().getGroupKey();
-                    if (mBubbleData.getBubblesInGroup(
-                            groupKey, mNotificationEntryManager).isEmpty()) {
+                    if (getBubblesInGroup(groupKey).isEmpty()) {
                         // Time to potentially remove the summary
                         for (NotifCallback cb : mCallbacks) {
                             cb.maybeCancelSummary(entry);
@@ -1449,8 +1511,7 @@
         }
 
         String groupKey = entry.getSbn().getGroupKey();
-        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(
-                groupKey, mNotificationEntryManager);
+        ArrayList<Bubble> bubbleChildren = getBubblesInGroup(groupKey);
         boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
                 && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
         boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
@@ -1694,4 +1755,10 @@
             }
         }
     }
+
+    static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
+        return new BubbleEntry(e.getSbn(), e.getRanking(), e.isClearable(),
+                e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
+                e.shouldSuppressPeek());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 2c3cb5f..55ecb22 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -36,8 +36,6 @@
 import com.android.systemui.bubbles.BubbleController.DismissReason;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -52,8 +50,6 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
-import javax.inject.Inject;
-
 /**
  * Keeps track of active bubbles.
  */
@@ -154,12 +150,11 @@
      * associated with it). This list is used to check if the summary should be hidden from the
      * shade.
      *
-     * Key: group key of the NotificationEntry
-     * Value: key of the NotificationEntry
+     * Key: group key of the notification
+     * Value: key of the notification
      */
     private HashMap<String, String> mSuppressedGroupKeys = new HashMap<>();
 
-    @Inject
     public BubbleData(Context context) {
         mContext = context;
         mBubbles = new ArrayList<>();
@@ -205,6 +200,11 @@
         return mSelectedBubble;
     }
 
+    /** Return a read-only current active bubble lists. */
+    public List<Bubble> getActiveBubbles() {
+        return Collections.unmodifiableList(mBubbles);
+    }
+
     public void setExpanded(boolean expanded) {
         if (DEBUG_BUBBLE_DATA) {
             Log.d(TAG, "setExpanded: " + expanded);
@@ -235,8 +235,8 @@
      * @param persistedBubble The bubble to use, only non-null if it's a bubble being promoted from
      *              the overflow that was persisted over reboot.
      */
-    Bubble getOrCreateBubble(NotificationEntry entry, Bubble persistedBubble) {
-        String key = entry != null ? entry.getKey() : persistedBubble.getKey();
+    public Bubble getOrCreateBubble(BubbleEntry entry, Bubble persistedBubble) {
+        String key = persistedBubble != null ? persistedBubble.getKey() : entry.getKey();
         Bubble bubbleToReturn = getBubbleInStackWithKey(key);
 
         if (bubbleToReturn == null) {
@@ -266,7 +266,7 @@
     /**
      * When this method is called it is expected that all info in the bubble has completed loading.
      * @see Bubble#inflate(BubbleViewInfoTask.Callback, Context,
-     * BubbleStackView, BubbleIconFactory).
+     * BubbleStackView, BubbleIconFactory, boolean).
      */
     void notificationEntryUpdated(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
         if (DEBUG_BUBBLE_DATA) {
@@ -329,7 +329,7 @@
      * Retrieves the notif entry key of the summary associated with the provided group key.
      *
      * @param groupKey the group to look up
-     * @return the key for the {@link NotificationEntry} that is the summary of this group.
+     * @return the key for the notification that is the summary of this group.
      */
     String getSummaryKey(String groupKey) {
         return mSuppressedGroupKeys.get(groupKey);
@@ -350,25 +350,6 @@
     }
 
     /**
-     * Retrieves any bubbles that are part of the notification group represented by the provided
-     * group key.
-     */
-    ArrayList<Bubble> getBubblesInGroup(@Nullable String groupKey, @NonNull
-            NotificationEntryManager nem) {
-        ArrayList<Bubble> bubbleChildren = new ArrayList<>();
-        if (groupKey == null) {
-            return bubbleChildren;
-        }
-        for (Bubble b : mBubbles) {
-            final NotificationEntry entry = nem.getPendingOrActiveNotif(b.getKey());
-            if (entry != null && groupKey.equals(entry.getSbn().getGroupKey())) {
-                bubbleChildren.add(b);
-            }
-        }
-        return bubbleChildren;
-    }
-
-    /**
      * Removes bubbles from the given package whose shortcut are not in the provided list of valid
      * shortcuts.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
new file mode 100644
index 0000000..6a13025
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleEntry.java
@@ -0,0 +1,98 @@
+/*
+ * 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.app.Notification.BubbleMetadata;
+import android.app.NotificationManager.Policy;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Represents a notification with needed data and flag for bubbles.
+ *
+ * @see Bubble
+ */
+public class BubbleEntry {
+
+    private StatusBarNotification mSbn;
+    private Ranking mRanking;
+
+    private boolean mIsClearable;
+    private boolean mShouldSuppressNotificationDot;
+    private boolean mShouldSuppressNotificationList;
+    private boolean mShouldSuppressPeek;
+
+    public BubbleEntry(@NonNull StatusBarNotification sbn,
+            Ranking ranking, boolean isClearable, boolean shouldSuppressNotificationDot,
+            boolean shouldSuppressNotificationList, boolean shouldSuppressPeek) {
+        mSbn = sbn;
+        mRanking = ranking;
+
+        mIsClearable = isClearable;
+        mShouldSuppressNotificationDot = shouldSuppressNotificationDot;
+        mShouldSuppressNotificationList = shouldSuppressNotificationList;
+        mShouldSuppressPeek = shouldSuppressPeek;
+    }
+
+    /** @return the {@link StatusBarNotification} for this entry. */
+    @NonNull
+    public StatusBarNotification getStatusBarNotification() {
+        return mSbn;
+    }
+
+    /** @return the {@link Ranking} for this entry. */
+    public Ranking getRanking() {
+        return mRanking;
+    }
+
+    /** @return the key in the {@link StatusBarNotification}. */
+    public String getKey() {
+        return mSbn.getKey();
+    }
+
+    /** @return the {@link BubbleMetadata} in the {@link StatusBarNotification}. */
+    @Nullable
+    public BubbleMetadata getBubbleMetadata() {
+        return getStatusBarNotification().getNotification().getBubbleMetadata();
+    }
+
+    /** @return true if this notification is clearable. */
+    public boolean isClearable() {
+        return mIsClearable;
+    }
+
+    /** @return true if {@link Policy#SUPPRESSED_EFFECT_BADGE} set for this notification. */
+    public boolean shouldSuppressNotificationDot() {
+        return mShouldSuppressNotificationDot;
+    }
+
+    /**
+     * @return true if {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+     * set for this notification.
+     */
+    public boolean shouldSuppressNotificationList() {
+        return mShouldSuppressNotificationList;
+    }
+
+    /** @return true if {@link Policy#SUPPRESSED_EFFECT_PEEK} set for this notification. */
+    public boolean shouldSuppressPeek() {
+        return mShouldSuppressPeek;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 1fa3aaa..69f7828 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -34,6 +34,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.text.TextUtils;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -212,6 +213,14 @@
         super.onDraw(canvas);
     }
 
+    void updateFontSize(float fontScale) {
+        final float fontSize = mContext.getResources()
+                .getDimensionPixelSize(com.android.internal.R.dimen.text_size_body_2_material);
+        final float newFontSize = fontSize * fontScale;
+        mMessageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
+        mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
+    }
+
     /** Configures the flyout, collapsed into to dot form. */
     void setupFlyoutStartingAsDot(
             Bubble.FlyoutMessage flyoutMessage,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 0dcd1d2..e83954b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1156,6 +1156,10 @@
         addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
     }
 
+    void updateFlyout(float fontScale) {
+        mFlyout.updateFontSize(fontScale);
+    }
+
     private void updateOverflow() {
         mBubbleOverflow.update();
         mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 28757fa..010a29e 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -22,8 +22,6 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.Person;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -36,8 +34,6 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
-import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.PathParser;
 import android.view.LayoutInflater;
@@ -47,10 +43,8 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.lang.ref.WeakReference;
-import java.util.List;
 import java.util.Objects;
 
 /**
@@ -208,73 +202,6 @@
         }
     }
 
-
-    /**
-     * Returns our best guess for the most relevant text summary of the latest update to this
-     * notification, based on its type. Returns null if there should not be an update message.
-     */
-    @NonNull
-    static Bubble.FlyoutMessage extractFlyoutMessage(NotificationEntry entry) {
-        Objects.requireNonNull(entry);
-        final Notification underlyingNotif = entry.getSbn().getNotification();
-        final Class<? extends Notification.Style> style = underlyingNotif.getNotificationStyle();
-
-        Bubble.FlyoutMessage bubbleMessage = new Bubble.FlyoutMessage();
-        bubbleMessage.isGroupChat = underlyingNotif.extras.getBoolean(
-                Notification.EXTRA_IS_GROUP_CONVERSATION);
-        try {
-            if (Notification.BigTextStyle.class.equals(style)) {
-                // Return the big text, it is big so probably important. If it's not there use the
-                // normal text.
-                CharSequence bigText =
-                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
-                bubbleMessage.message = !TextUtils.isEmpty(bigText)
-                        ? bigText
-                        : underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-                return bubbleMessage;
-            } else if (Notification.MessagingStyle.class.equals(style)) {
-                final List<Notification.MessagingStyle.Message> messages =
-                        Notification.MessagingStyle.Message.getMessagesFromBundleArray(
-                                (Parcelable[]) underlyingNotif.extras.get(
-                                        Notification.EXTRA_MESSAGES));
-
-                final Notification.MessagingStyle.Message latestMessage =
-                        Notification.MessagingStyle.findLatestIncomingMessage(messages);
-                if (latestMessage != null) {
-                    bubbleMessage.message = latestMessage.getText();
-                    Person sender = latestMessage.getSenderPerson();
-                    bubbleMessage.senderName = sender != null ? sender.getName() : null;
-                    bubbleMessage.senderAvatar = null;
-                    bubbleMessage.senderIcon = sender != null ? sender.getIcon() : null;
-                    return bubbleMessage;
-                }
-            } else if (Notification.InboxStyle.class.equals(style)) {
-                CharSequence[] lines =
-                        underlyingNotif.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES);
-
-                // Return the last line since it should be the most recent.
-                if (lines != null && lines.length > 0) {
-                    bubbleMessage.message = lines[lines.length - 1];
-                    return bubbleMessage;
-                }
-            } else if (Notification.MediaStyle.class.equals(style)) {
-                // Return nothing, media updates aren't typically useful as a text update.
-                return bubbleMessage;
-            } else {
-                // Default to text extra.
-                bubbleMessage.message =
-                        underlyingNotif.extras.getCharSequence(Notification.EXTRA_TEXT);
-                return bubbleMessage;
-            }
-        } catch (ClassCastException | NullPointerException | ArrayIndexOutOfBoundsException e) {
-            // No use crashing, we'll just return null and the caller will assume there's no update
-            // message.
-            e.printStackTrace();
-        }
-
-        return bubbleMessage;
-    }
-
     @Nullable
     static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
         Objects.requireNonNull(context);
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 d2eaf0d..00ae3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -23,7 +23,6 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleData;
 import com.android.systemui.bubbles.BubbleDataRepository;
 import com.android.systemui.bubbles.Bubbles;
 import com.android.systemui.dagger.SysUISingleton;
@@ -59,7 +58,6 @@
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
             ShadeController shadeController,
-            BubbleData data,
             ConfigurationController configurationController,
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
@@ -77,12 +75,11 @@
             WindowManager windowManager,
             WindowManagerShellWrapper windowManagerShellWrapper,
             LauncherApps launcherApps) {
-        return new BubbleController(
+        return BubbleController.create(
                 context,
                 notificationShadeWindowController,
                 statusBarStateController,
                 shadeController,
-                data,
                 null /* synchronizer */,
                 configurationController,
                 interruptionStateProvider,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index fa33284..1e239b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -17,30 +17,44 @@
 package com.android.systemui.qs;
 
 import com.android.systemui.R;
+import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
 
-public class QSContainerImplController {
-    private final QSContainerImpl mView;
+class QSContainerImplController extends ViewController<QSContainerImpl> {
     private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
 
     private QSContainerImplController(QSContainerImpl view,
             QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) {
-        mView = view;
+        super(view);
         mQuickStatusBarHeaderController = quickStatusBarHeaderControllerBuilder
                 .setQuickStatusBarHeader(mView.findViewById(R.id.header)).build();
     }
 
+    @Override
+    public void init() {
+        super.init();
+        mQuickStatusBarHeaderController.init();
+    }
+
     public void setListening(boolean listening) {
         mQuickStatusBarHeaderController.setListening(listening);
     }
 
-    public static class Builder {
+    @Override
+    protected void onViewAttached() {
+    }
+
+    @Override
+    protected void onViewDetached() {
+    }
+
+    static class Builder {
         private final QuickStatusBarHeaderController.Builder mQuickStatusBarHeaderControllerBuilder;
         private QSContainerImpl mView;
 
         @Inject
-        public Builder(
+        Builder(
                 QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) {
             mQuickStatusBarHeaderControllerBuilder = quickStatusBarHeaderControllerBuilder;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index f1bb899..3a78365 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -142,7 +142,7 @@
         mQSContainerImplController = mQSContainerImplControllerBuilder
                 .setQSContainerImpl((QSContainerImpl) view)
                 .build();
-
+        mQSContainerImplController.init();
 
         mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
         mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
@@ -367,14 +367,13 @@
         if (DEBUG) Log.d(TAG, "setListening " + listening);
         mListening = listening;
         mQSContainerImplController.setListening(listening);
-        mHeader.setListening(listening);
         mFooter.setListening(listening);
         mQSPanel.setListening(mListening, mQsExpanded);
     }
 
     @Override
     public void setHeaderListening(boolean listening) {
-        mHeader.setListening(listening);
+        mQSContainerImplController.setListening(listening);
         mFooter.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 544249a..a9fbc74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,27 +17,16 @@
 import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
 import android.annotation.ColorInt;
-import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.media.AudioManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.AlarmClock;
-import android.provider.Settings;
-import android.service.notification.ZenModeConfig;
-import android.text.format.DateUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.MathUtils;
 import android.util.Pair;
 import android.view.ContextThemeWrapper;
@@ -53,93 +42,48 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 
-import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
 import com.android.systemui.DualToneHandler;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.demomode.DemoModeController;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.privacy.PrivacyChipEvent;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.QSDetail.Callback;
-import com.android.systemui.qs.carrier.QSCarrierGroup;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
-import com.android.systemui.statusbar.policy.DateView;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.RingerModeTracker;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * View that contains the top-most bits of the screen (primarily the status bar with date, time, and
  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
  * contents.
  */
-public class QuickStatusBarHeader extends RelativeLayout implements
-        View.OnClickListener, NextAlarmController.NextAlarmChangeCallback,
-        ZenModeController.Callback, LifecycleOwner {
-    private static final String TAG = "QuickStatusBarHeader";
-    private static final boolean DEBUG = false;
+public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwner {
 
-    /** Delay for auto fading out the long press tooltip after it's fully visible (in ms). */
-    private static final long AUTO_FADE_OUT_DELAY_MS = DateUtils.SECOND_IN_MILLIS * 6;
-    private static final int FADE_ANIMATION_DURATION_MS = 300;
-    private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
     public static final int MAX_TOOLTIP_SHOWN_COUNT = 2;
 
-    private final NextAlarmController mAlarmController;
-    private final ZenModeController mZenController;
-    private final StatusBarIconController mStatusBarIconController;
-    private final ActivityStarter mActivityStarter;
-
-    private QSPanel mQsPanel;
-
     private boolean mExpanded;
-    private boolean mListening;
     private boolean mQsDisabled;
 
-    private QSCarrierGroup mCarrierGroup;
     protected QuickQSPanel mHeaderQsPanel;
-    protected QSTileHost mHost;
-    private TintedIconManager mIconManager;
     private TouchAnimator mStatusIconsAlphaAnimator;
     private TouchAnimator mHeaderTextContainerAlphaAnimator;
     private TouchAnimator mPrivacyChipAlphaAnimator;
     private DualToneHandler mDualToneHandler;
-    private final CommandQueue mCommandQueue;
 
     private View mSystemIconsView;
     private View mQuickQsStatusIcons;
     private View mHeaderTextContainerView;
 
-    private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
-    private AlarmManager.AlarmClockInfo mNextAlarm;
-
     private ImageView mNextAlarmIcon;
     /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
     private TextView mNextAlarmTextView;
@@ -149,23 +93,13 @@
     private TextView mRingerModeTextView;
     private View mRingerContainer;
     private Clock mClockView;
-    private DateView mDateView;
     private OngoingPrivacyChip mPrivacyChip;
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
-    private RingerModeTracker mRingerModeTracker;
-    private DemoModeController mDemoModeController;
-    private DemoMode mDemoModeReceiver;
-    private UserTracker mUserTracker;
-    private boolean mAllIndicatorsEnabled;
-    private boolean mMicCameraIndicatorsEnabled;
 
-    private PrivacyItemController mPrivacyItemController;
-    private final UiEventLogger mUiEventLogger;
     // Used for RingerModeTracker
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
 
-    private boolean mHasTopCutout = false;
     private int mStatusBarPaddingTop = 0;
     private int mRoundedCornerPadding = 0;
     private int mContentMarginStart;
@@ -175,59 +109,11 @@
     private int mCutOutPaddingRight;
     private float mExpandedHeaderAlpha = 1.0f;
     private float mKeyguardExpansionFraction;
-    private boolean mPrivacyChipLogged = false;
 
-    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
-        @Override
-        public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
-            mPrivacyChip.setPrivacyList(privacyItems);
-            setChipVisibility(!privacyItems.isEmpty());
-        }
-
-        @Override
-        public void onFlagAllChanged(boolean flag) {
-            if (mAllIndicatorsEnabled != flag) {
-                mAllIndicatorsEnabled = flag;
-                update();
-            }
-        }
-
-        @Override
-        public void onFlagMicCameraChanged(boolean flag) {
-            if (mMicCameraIndicatorsEnabled != flag) {
-                mMicCameraIndicatorsEnabled = flag;
-                update();
-            }
-        }
-
-        private void update() {
-            StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
-            iconContainer.setIgnoredSlots(getIgnoredIconSlots());
-            setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
-        }
-    };
-
-    @Inject
-    public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            NextAlarmController nextAlarmController, ZenModeController zenModeController,
-            StatusBarIconController statusBarIconController,
-            ActivityStarter activityStarter, PrivacyItemController privacyItemController,
-            CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
-            UiEventLogger uiEventLogger, DemoModeController demoModeController,
-            UserTracker userTracker) {
+    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mAlarmController = nextAlarmController;
-        mZenController = zenModeController;
-        mStatusBarIconController = statusBarIconController;
-        mActivityStarter = activityStarter;
-        mPrivacyItemController = privacyItemController;
         mDualToneHandler = new DualToneHandler(
                 new ContextThemeWrapper(context, R.style.QSHeaderTheme));
-        mCommandQueue = commandQueue;
-        mRingerModeTracker = ringerModeTracker;
-        mUiEventLogger = uiEventLogger;
-        mDemoModeController = demoModeController;
-        mUserTracker = userTracker;
     }
 
     @Override
@@ -237,11 +123,6 @@
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
-        StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
-        // Ignore privacy icons because they show in the space above QQS
-        iconContainer.addIgnoredSlots(getIgnoredIconSlots());
-        iconContainer.setShouldRestrictIcons(false);
-        mIconManager = new TintedIconManager(iconContainer, mCommandQueue);
 
         // Views corresponding to the header info section (e.g. ringer and next alarm).
         mHeaderTextContainerView = findViewById(R.id.header_text_container);
@@ -249,36 +130,18 @@
         mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
         mNextAlarmTextView = findViewById(R.id.next_alarm_text);
         mNextAlarmContainer = findViewById(R.id.alarm_container);
-        mNextAlarmContainer.setOnClickListener(this::onClick);
         mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
         mRingerModeTextView = findViewById(R.id.ringer_mode_text);
         mRingerContainer = findViewById(R.id.ringer_container);
-        mRingerContainer.setOnClickListener(this::onClick);
         mPrivacyChip = findViewById(R.id.privacy_chip);
-        mPrivacyChip.setOnClickListener(this::onClick);
-        mCarrierGroup = findViewById(R.id.carrier_group);
-
 
         updateResources();
 
         Rect tintArea = new Rect(0, 0, 0, 0);
-        int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
-                android.R.attr.colorForeground);
-        float intensity = getColorIntensity(colorForeground);
-        int fillColor = mDualToneHandler.getSingleColor(intensity);
-
         // Set light text on the header icons because they will always be on a black background
         applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
 
-        // Set the correct tint for the status icons so they contrast
-        mIconManager.setTint(fillColor);
-        mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
-        mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
-
         mClockView = findViewById(R.id.clock);
-        mClockView.setOnClickListener(this);
-        mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
-        mDateView = findViewById(R.id.date);
         mSpace = findViewById(R.id.space);
 
         // Tint for the battery icons are handled in setupHost()
@@ -290,33 +153,28 @@
         mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
+    }
 
-        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
-        mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+    void onAttach(TintedIconManager iconManager) {
+        int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
+                android.R.attr.colorForeground);
+        float intensity = getColorIntensity(colorForeground);
+        int fillColor = mDualToneHandler.getSingleColor(intensity);
+
+        // Set the correct tint for the status icons so they contrast
+        iconManager.setTint(fillColor);
+        mNextAlarmIcon.setImageTintList(ColorStateList.valueOf(fillColor));
+        mRingerModeIcon.setImageTintList(ColorStateList.valueOf(fillColor));
     }
 
     public QuickQSPanel getHeaderQsPanel() {
         return mHeaderQsPanel;
     }
 
-    private List<String> getIgnoredIconSlots() {
-        ArrayList<String> ignored = new ArrayList<>();
-        if (getChipEnabled()) {
-            ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_camera));
-            ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_microphone));
-            if (mAllIndicatorsEnabled) {
-                ignored.add(mContext.getResources().getString(
-                        com.android.internal.R.string.status_bar_location));
-            }
-        }
-
-        return ignored;
-    }
-
-    private void updateStatusText() {
-        boolean changed = updateRingerStatus() || updateAlarmStatus();
+    void updateStatusText(int ringerMode, AlarmClockInfo nextAlarm, boolean zenOverridingRinger,
+            boolean use24HourFormat) {
+        boolean changed = updateRingerStatus(ringerMode, zenOverridingRinger)
+                || updateAlarmStatus(nextAlarm, use24HourFormat);
 
         if (changed) {
             boolean alarmVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
@@ -326,32 +184,17 @@
         }
     }
 
-    private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible && getChipEnabled()) {
-            mPrivacyChip.setVisibility(View.VISIBLE);
-            // Makes sure that the chip is logged as viewed at most once each time QS is opened
-            // mListening makes sure that the callback didn't return after the user closed QS
-            if (!mPrivacyChipLogged && mListening) {
-                mPrivacyChipLogged = true;
-                mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
-            }
-        } else {
-            mPrivacyChip.setVisibility(View.GONE);
-        }
-    }
-
-    private boolean updateRingerStatus() {
+    private boolean updateRingerStatus(int ringerMode, boolean zenOverridingRinger) {
         boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
         CharSequence originalRingerText = mRingerModeTextView.getText();
 
         boolean ringerVisible = false;
-        if (!ZenModeConfig.isZenOverridingRinger(mZenController.getZen(),
-                mZenController.getConsolidatedPolicy())) {
-            if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+        if (!zenOverridingRinger) {
+            if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
                 mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
                 mRingerModeTextView.setText(R.string.qs_status_phone_vibrate);
                 ringerVisible = true;
-            } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) {
+            } else if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
                 mRingerModeIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                 mRingerModeTextView.setText(R.string.qs_status_phone_muted);
                 ringerVisible = true;
@@ -365,14 +208,14 @@
                 !Objects.equals(originalRingerText, mRingerModeTextView.getText());
     }
 
-    private boolean updateAlarmStatus() {
+    private boolean updateAlarmStatus(AlarmClockInfo nextAlarm, boolean use24HourFormat) {
         boolean isOriginalVisible = mNextAlarmTextView.getVisibility() == View.VISIBLE;
         CharSequence originalAlarmText = mNextAlarmTextView.getText();
 
         boolean alarmVisible = false;
-        if (mNextAlarm != null) {
+        if (nextAlarm != null) {
             alarmVisible = true;
-            mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm));
+            mNextAlarmTextView.setText(formatNextAlarm(nextAlarm, use24HourFormat));
         }
         mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
         mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
@@ -419,7 +262,7 @@
         setMinimumHeight(sbHeight + qqsHeight);
     }
 
-    private void updateResources() {
+    void updateResources() {
         Resources resources = mContext.getResources();
         updateMinimumHeight();
 
@@ -529,18 +372,6 @@
     }
 
     @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mRingerModeTracker.getRingerModeInternal().observe(this, ringer -> {
-            mRingerMode = ringer;
-            updateStatusText();
-        });
-        mStatusBarIconController.addIconGroup(mIconManager);
-        mDemoModeController.addCallback(mDemoModeReceiver);
-        requestApplyInsets();
-    }
-
-    @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         // Handle padding of the clock
         DisplayCutout cutout = insets.getDisplayCutout();
@@ -563,17 +394,14 @@
         if (cutout != null) {
             Rect topCutout = cutout.getBoundingRectTop();
             if (topCutout.isEmpty() || cornerCutout) {
-                mHasTopCutout = false;
                 lp.width = 0;
                 mSpace.setVisibility(View.GONE);
             } else {
-                mHasTopCutout = true;
                 lp.width = topCutout.width();
                 mSpace.setVisibility(View.VISIBLE);
             }
         }
         mSpace.setLayoutParams(lp);
-        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
         mCutOutPaddingLeft = padding.first;
         mCutOutPaddingRight = padding.second;
         mWaterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
@@ -611,103 +439,14 @@
                 0);
     }
 
-    @Override
-    @VisibleForTesting
-    public void onDetachedFromWindow() {
-        setListening(false);
-        mRingerModeTracker.getRingerModeInternal().removeObservers(this);
-        mStatusBarIconController.removeIconGroup(mIconManager);
-        mDemoModeController.removeCallback(mDemoModeReceiver);
-        super.onDetachedFromWindow();
-    }
-
-    public void setListening(boolean listening) {
-        if (listening == mListening) {
-            return;
-        }
-        mHeaderQsPanel.setListening(listening);
-        if (mHeaderQsPanel.switchTileLayout()) {
-            updateResources();
-        }
-        mListening = listening;
-
-        if (listening) {
-            mZenController.addCallback(this);
-            mAlarmController.addCallback(this);
-            mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
-            // Get the most up to date info
-            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
-            mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
-            mPrivacyItemController.addCallback(mPICCallback);
-        } else {
-            mZenController.removeCallback(this);
-            mAlarmController.removeCallback(this);
-            mLifecycle.setCurrentState(Lifecycle.State.CREATED);
-            mPrivacyItemController.removeCallback(mPICCallback);
-            mPrivacyChipLogged = false;
-        }
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mClockView) {
-            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                    AlarmClock.ACTION_SHOW_ALARMS), 0);
-        } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
-            if (mNextAlarm.getShowIntent() != null) {
-                mActivityStarter.postStartActivityDismissingKeyguard(
-                        mNextAlarm.getShowIntent());
-            } else {
-                Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
-                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                        AlarmClock.ACTION_SHOW_ALARMS), 0);
-            }
-        } else if (v == mPrivacyChip) {
-            // If the privacy chip is visible, it means there were some indicators
-            Handler mUiHandler = new Handler(Looper.getMainLooper());
-            mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
-            mUiHandler.post(() -> {
-                mActivityStarter.postStartActivityDismissingKeyguard(
-                        new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
-                mHost.collapsePanels();
-            });
-        } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
-            mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
-                    Settings.ACTION_SOUND_SETTINGS), 0);
-        }
-    }
-
-    @Override
-    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarm = nextAlarm;
-        updateStatusText();
-    }
-
-    @Override
-    public void onZenChanged(int zen) {
-        updateStatusText();
-    }
-
-    @Override
-    public void onConfigChanged(ZenModeConfig config) {
-        updateStatusText();
-    }
-
     public void updateEverything() {
         post(() -> setClickable(!mExpanded));
     }
 
     public void setQSPanel(final QSPanel qsPanel) {
-        mQsPanel = qsPanel;
-        setupHost(qsPanel.getHost());
-    }
-
-    public void setupHost(final QSTileHost host) {
-        mHost = host;
         //host.setHeaderView(mExpandIndicator);
-        mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
-        mHeaderQsPanel.setHost(host, null /* No customization in header */);
-
+        mHeaderQsPanel.setQSPanelAndHeader(qsPanel, this);
+        mHeaderQsPanel.setHost(qsPanel.getHost(), null /* No customization in header */);
 
         Rect tintArea = new Rect(0, 0, 0, 0);
         int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
@@ -721,12 +460,11 @@
         mHeaderQsPanel.setCallback(qsPanelCallback);
     }
 
-    private String formatNextAlarm(AlarmManager.AlarmClockInfo info) {
+    private String formatNextAlarm(AlarmClockInfo info, boolean use24HourFormat) {
         if (info == null) {
             return "";
         }
-        String skeleton = android.text.format.DateFormat
-                .is24HourFormat(mContext, mUserTracker.getUserId()) ? "EHm" : "Ehma";
+        String skeleton = use24HourFormat ? "EHm" : "Ehma";
         String pattern = android.text.format.DateFormat
                 .getBestDateTimePattern(Locale.getDefault(), skeleton);
         return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString();
@@ -777,37 +515,4 @@
             updateHeaderTextContainerAlphaAnimator();
         }
     }
-
-    private boolean getChipEnabled() {
-        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
-    }
-
-    private static class ClockDemoModeReceiver implements DemoMode {
-        private Clock mClockView;
-
-        @Override
-        public List<String> demoCommands() {
-            return List.of(COMMAND_CLOCK);
-        }
-
-        ClockDemoModeReceiver(Clock clockView) {
-            mClockView = clockView;
-        }
-
-        @Override
-        public void dispatchDemoCommand(String command, Bundle args) {
-            mClockView.dispatchDemoCommand(command, args);
-        }
-
-        @Override
-        public void onDemoModeStarted() {
-            mClockView.onDemoModeStarted();
-        }
-
-        @Override
-        public void onDemoModeFinished() {
-            mClockView.onDemoModeFinished();
-        }
-    }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index d899acb..676a300 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -16,36 +16,393 @@
 
 package com.android.systemui.qs;
 
+import android.app.AlarmManager.AlarmClockInfo;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.AlarmClock;
+import android.provider.Settings;
+import android.service.notification.ZenModeConfig;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.privacy.OngoingPrivacyChip;
+import com.android.systemui.privacy.PrivacyChipEvent;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
 import com.android.systemui.qs.carrier.QSCarrierGroupController;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusIconContainer;
+import com.android.systemui.statusbar.policy.Clock;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeController.Callback;
+import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.ViewController;
+
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.inject.Inject;
 
-public class QuickStatusBarHeaderController {
-    private final QuickStatusBarHeader mView;
+/**
+ * Controller for {@link QuickStatusBarHeader}.
+ */
+class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> {
+    private static final String TAG = "QuickStatusBarHeader";
+
+    private final ZenModeController mZenModeController;
+    private final NextAlarmController mNextAlarmController;
+    private final PrivacyItemController mPrivacyItemController;
+    private final RingerModeTracker mRingerModeTracker;
+    private final ActivityStarter mActivityStarter;
+    private final UiEventLogger mUiEventLogger;
     private final QSCarrierGroupController mQSCarrierGroupController;
+    private final QuickQSPanel mHeaderQsPanel;
+    private final LifecycleRegistry mLifecycle;
+    private final OngoingPrivacyChip mPrivacyChip;
+    private final Clock mClockView;
+    private final View mNextAlarmContainer;
+    private final View mRingerContainer;
+    private final QSTileHost mQSTileHost;
+    private final StatusBarIconController mStatusBarIconController;
+    private final CommandQueue mCommandQueue;
+    private final DemoModeController mDemoModeController;
+    private final UserTracker mUserTracker;
+    private final StatusIconContainer mIconContainer;
+    private final StatusBarIconController.TintedIconManager mIconManager;
+    private final DemoMode mDemoModeReceiver;
+
+    private boolean mListening;
+    private AlarmClockInfo mNextAlarm;
+    private boolean mAllIndicatorsEnabled;
+    private boolean mMicCameraIndicatorsEnabled;
+    private boolean mPrivacyChipLogged;
+    private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
+
+    private final ZenModeController.Callback mZenModeControllerCallback = new Callback() {
+        @Override
+        public void onZenChanged(int zen) {
+            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
+                    use24HourFormat());
+        }
+
+        @Override
+        public void onConfigChanged(ZenModeConfig config) {
+            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
+                    use24HourFormat());
+        }
+    };
+
+    private boolean use24HourFormat() {
+        return android.text.format.DateFormat.is24HourFormat(
+                mView.getContext(), mUserTracker.getUserId());
+
+    }
+
+    private final NextAlarmChangeCallback mNextAlarmChangeCallback = new NextAlarmChangeCallback() {
+        @Override
+        public void onNextAlarmChanged(AlarmClockInfo nextAlarm) {
+            mNextAlarm = nextAlarm;
+            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
+                    use24HourFormat());
+        }
+    };
+
+    private final LifecycleOwner mLifecycleOwner = new LifecycleOwner() {
+        @NonNull
+        @Override
+        public Lifecycle getLifecycle() {
+            return mLifecycle;
+        }
+    };
+
+    private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+        @Override
+        public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
+            mPrivacyChip.setPrivacyList(privacyItems);
+            setChipVisibility(!privacyItems.isEmpty());
+        }
+
+        @Override
+        public void onFlagAllChanged(boolean flag) {
+            if (mAllIndicatorsEnabled != flag) {
+                mAllIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
+        @Override
+        public void onFlagMicCameraChanged(boolean flag) {
+            if (mMicCameraIndicatorsEnabled != flag) {
+                mMicCameraIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
+        private void update() {
+            StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons);
+            iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+            setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+        }
+    };
+
+    private View.OnClickListener mOnClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (v == mClockView) {
+                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
+                        AlarmClock.ACTION_SHOW_ALARMS), 0);
+            } else if (v == mNextAlarmContainer && mNextAlarmContainer.isVisibleToUser()) {
+                if (mNextAlarm.getShowIntent() != null) {
+                    mActivityStarter.postStartActivityDismissingKeyguard(
+                            mNextAlarm.getShowIntent());
+                } else {
+                    Log.d(TAG, "No PendingIntent for next alarm. Using default intent");
+                    mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
+                            AlarmClock.ACTION_SHOW_ALARMS), 0);
+                }
+            } else if (v == mPrivacyChip) {
+                // If the privacy chip is visible, it means there were some indicators
+                Handler mUiHandler = new Handler(Looper.getMainLooper());
+                mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK);
+                mUiHandler.post(() -> {
+                    mActivityStarter.postStartActivityDismissingKeyguard(
+                            new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
+                    mQSTileHost.collapsePanels();
+                });
+            } else if (v == mRingerContainer && mRingerContainer.isVisibleToUser()) {
+                mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
+                        Settings.ACTION_SOUND_SETTINGS), 0);
+            }
+        }
+    };
 
     private QuickStatusBarHeaderController(QuickStatusBarHeader view,
+            ZenModeController zenModeController, NextAlarmController nextAlarmController,
+            PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+            ActivityStarter activityStarter, UiEventLogger uiEventLogger,
+            QSTileHost qsTileHost, StatusBarIconController statusBarIconController,
+            CommandQueue commandQueue, DemoModeController demoModeController,
+            UserTracker userTracker,
             QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
-        mView = view;
+        super(view);
+        mZenModeController = zenModeController;
+        mNextAlarmController = nextAlarmController;
+        mPrivacyItemController = privacyItemController;
+        mRingerModeTracker = ringerModeTracker;
+        mActivityStarter = activityStarter;
+        mUiEventLogger = uiEventLogger;
+        mQSTileHost = qsTileHost;
+        mStatusBarIconController = statusBarIconController;
+        mCommandQueue = commandQueue;
+        mDemoModeController = demoModeController;
+        mUserTracker = userTracker;
+        mLifecycle = new LifecycleRegistry(mLifecycleOwner);
+
         mQSCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(mView.findViewById(R.id.carrier_group))
                 .build();
+
+
+        mPrivacyChip = mView.findViewById(R.id.privacy_chip);
+        mHeaderQsPanel = mView.findViewById(R.id.quick_qs_panel);
+        mNextAlarmContainer = mView.findViewById(R.id.alarm_container);
+        mClockView = mView.findViewById(R.id.clock);
+        mRingerContainer = mView.findViewById(R.id.ringer_container);
+        mIconContainer = mView.findViewById(R.id.statusIcons);
+
+        mIconManager = new StatusBarIconController.TintedIconManager(mIconContainer, mCommandQueue);
+        mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mRingerModeTracker.getRingerModeInternal().observe(mLifecycleOwner, ringer -> {
+            mRingerMode = ringer;
+            mView.updateStatusText(mRingerMode, mNextAlarm, isZenOverridingRinger(),
+                    use24HourFormat());
+        });
+
+        mClockView.setOnClickListener(mOnClickListener);
+        mNextAlarmContainer.setOnClickListener(mOnClickListener);
+        mRingerContainer.setOnClickListener(mOnClickListener);
+        mPrivacyChip.setOnClickListener(mOnClickListener);
+
+        // Ignore privacy icons because they show in the space above QQS
+        mIconContainer.addIgnoredSlots(getIgnoredIconSlots());
+        mIconContainer.setShouldRestrictIcons(false);
+        mStatusBarIconController.addIconGroup(mIconManager);
+
+        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+        mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+
+        setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
+
+        mView.onAttach(mIconManager);
+
+        mDemoModeController.addCallback(mDemoModeReceiver);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mRingerModeTracker.getRingerModeInternal().removeObservers(mLifecycleOwner);
+        mClockView.setOnClickListener(null);
+        mNextAlarmContainer.setOnClickListener(null);
+        mRingerContainer.setOnClickListener(null);
+        mPrivacyChip.setOnClickListener(null);
+        mStatusBarIconController.removeIconGroup(mIconManager);
+        mDemoModeController.removeCallback(mDemoModeReceiver);
+        setListening(false);
     }
 
     public void setListening(boolean listening) {
         mQSCarrierGroupController.setListening(listening);
-        // TODO: move mView.setListening logic into here.
-        mView.setListening(listening);
+
+        if (listening == mListening) {
+            return;
+        }
+        mListening = listening;
+
+        mHeaderQsPanel.setListening(listening);
+        if (mHeaderQsPanel.switchTileLayout()) {
+            mView.updateResources();
+        }
+
+        if (listening) {
+            mZenModeController.addCallback(mZenModeControllerCallback);
+            mNextAlarmController.addCallback(mNextAlarmChangeCallback);
+            mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
+            // Get the most up to date info
+            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+            mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
+            mPrivacyItemController.addCallback(mPICCallback);
+        } else {
+            mZenModeController.removeCallback(mZenModeControllerCallback);
+            mNextAlarmController.removeCallback(mNextAlarmChangeCallback);
+            mLifecycle.setCurrentState(Lifecycle.State.CREATED);
+            mPrivacyItemController.removeCallback(mPICCallback);
+            mPrivacyChipLogged = false;
+        }
+    }
+
+    private void setChipVisibility(boolean chipVisible) {
+        if (chipVisible && getChipEnabled()) {
+            mPrivacyChip.setVisibility(View.VISIBLE);
+            // Makes sure that the chip is logged as viewed at most once each time QS is opened
+            // mListening makes sure that the callback didn't return after the user closed QS
+            if (!mPrivacyChipLogged && mListening) {
+                mPrivacyChipLogged = true;
+                mUiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_VIEW);
+            }
+        } else {
+            mPrivacyChip.setVisibility(View.GONE);
+        }
+    }
+
+    private List<String> getIgnoredIconSlots() {
+        ArrayList<String> ignored = new ArrayList<>();
+        if (getChipEnabled()) {
+            ignored.add(mView.getResources().getString(
+                    com.android.internal.R.string.status_bar_camera));
+            ignored.add(mView.getResources().getString(
+                    com.android.internal.R.string.status_bar_microphone));
+            if (mAllIndicatorsEnabled) {
+                ignored.add(mView.getResources().getString(
+                        com.android.internal.R.string.status_bar_location));
+            }
+        }
+
+        return ignored;
+    }
+
+    private boolean getChipEnabled() {
+        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+    }
+
+    private boolean isZenOverridingRinger() {
+        return ZenModeConfig.isZenOverridingRinger(mZenModeController.getZen(),
+                mZenModeController.getConsolidatedPolicy());
     }
 
 
-    public static class Builder {
+    private static class ClockDemoModeReceiver implements DemoMode {
+        private Clock mClockView;
+
+        @Override
+        public List<String> demoCommands() {
+            return List.of(COMMAND_CLOCK);
+        }
+
+        ClockDemoModeReceiver(Clock clockView) {
+            mClockView = clockView;
+        }
+
+        @Override
+        public void dispatchDemoCommand(String command, Bundle args) {
+            mClockView.dispatchDemoCommand(command, args);
+        }
+
+        @Override
+        public void onDemoModeStarted() {
+            mClockView.onDemoModeStarted();
+        }
+
+        @Override
+        public void onDemoModeFinished() {
+            mClockView.onDemoModeFinished();
+        }
+    }
+
+    static class Builder {
+        private final ZenModeController mZenModeController;
+        private final NextAlarmController mNextAlarmController;
+        private final PrivacyItemController mPrivacyItemController;
+        private final RingerModeTracker mRingerModeTracker;
+        private final ActivityStarter mActivityStarter;
+        private final UiEventLogger mUiEventLogger;
+        private final QSTileHost mQsTileHost;
+        private final StatusBarIconController mStatusBarIconController;
+        private final CommandQueue mCommandQueue;
+        private final DemoModeController mDemoModeController;
+        private final UserTracker mUserTracker;
         private final QSCarrierGroupController.Builder mQSCarrierGroupControllerBuilder;
         private QuickStatusBarHeader mView;
 
         @Inject
-        public Builder(QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
+        Builder(ZenModeController zenModeController, NextAlarmController nextAlarmController,
+                PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker,
+                ActivityStarter activityStarter, UiEventLogger uiEventLogger, QSTileHost qsTileHost,
+                StatusBarIconController statusBarIconController, CommandQueue commandQueue,
+                DemoModeController demoModeController, UserTracker userTracker,
+                QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) {
+            mZenModeController = zenModeController;
+            mNextAlarmController = nextAlarmController;
+            mPrivacyItemController = privacyItemController;
+            mRingerModeTracker = ringerModeTracker;
+            mActivityStarter = activityStarter;
+            mUiEventLogger = uiEventLogger;
+            mQsTileHost = qsTileHost;
+            mStatusBarIconController = statusBarIconController;
+            mCommandQueue = commandQueue;
+            mDemoModeController = demoModeController;
+            mUserTracker = userTracker;
             mQSCarrierGroupControllerBuilder = qsCarrierGroupControllerBuilder;
         }
 
@@ -54,8 +411,13 @@
             return this;
         }
 
-        public QuickStatusBarHeaderController build() {
-            return new QuickStatusBarHeaderController(mView, mQSCarrierGroupControllerBuilder);
+
+        QuickStatusBarHeaderController build() {
+            return new QuickStatusBarHeaderController(mView, mZenModeController,
+                    mNextAlarmController, mPrivacyItemController, mRingerModeTracker,
+                    mActivityStarter, mUiEventLogger, mQsTileHost, mStatusBarIconController,
+                    mCommandQueue, mDemoModeController, mUserTracker,
+                    mQSCarrierGroupControllerBuilder);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 0ae1170..3b0415b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -35,12 +35,14 @@
 
 import android.annotation.FloatRange;
 import android.app.ActivityTaskManager;
+import android.app.PictureInPictureParams;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -503,6 +505,38 @@
             }
         }
 
+        @Override
+        public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+                PictureInPictureParams pictureInPictureParams,
+                int launcherRotation, int shelfHeight) {
+            if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) {
+                return null;
+            }
+            long binderToken = Binder.clearCallingIdentity();
+            try {
+                return mPipOptional.map(pip ->
+                        pip.startSwipePipToHome(componentName, activityInfo,
+                                pictureInPictureParams, launcherRotation, shelfHeight))
+                        .orElse(null);
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
+        @Override
+        public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+            if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) {
+                return;
+            }
+            long binderToken = Binder.clearCallingIdentity();
+            try {
+                mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome(
+                        componentName, destinationBounds));
+            } finally {
+                Binder.restoreCallingIdentity(binderToken);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
index df03c3e..0aa9d4d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenInternalAudioRecorder.java
@@ -48,6 +48,7 @@
     private long mTotalBytes;
     private MediaMuxer mMuxer;
     private boolean mMic;
+    private boolean mStarted;
 
     private int mTrackId = -1;
 
@@ -263,10 +264,14 @@
     * start recording
      * @throws IllegalStateException if recording fails to initialize
     */
-    public void start() throws IllegalStateException {
-        if (mThread != null) {
-            Log.e(TAG, "a recording is being done in parallel or stop is not called");
+    public synchronized void start() throws IllegalStateException {
+        if (mStarted) {
+            if (mThread == null) {
+                throw new IllegalStateException("Recording stopped and can't restart (single use)");
+            }
+            throw new IllegalStateException("Recording already started");
         }
+        mStarted = true;
         mAudioRecord.startRecording();
         if (mMic) mAudioRecordMic.startRecording();
         Log.d(TAG, "channel count " + mAudioRecord.getChannelCount());
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index a6cd350..344f0d2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -27,7 +27,6 @@
 import com.android.systemui.qs.QSFooterImpl;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QuickQSPanel;
-import com.android.systemui.qs.QuickStatusBarHeader;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 
@@ -93,10 +92,6 @@
         }
 
         /**
-         * Creates the QuickStatusBarHeader.
-         */
-        QuickStatusBarHeader createQsHeader();
-        /**
          * Creates the QSFooterImpl.
          */
         QSFooterImpl createQsFooter();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index 1e969c2..fa78d1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -36,6 +36,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -65,6 +66,8 @@
     @Mock
     private ModeSwitchesController mModeSwitchesController;
     @Mock
+    private NavigationModeController mNavigationModeController;
+    @Mock
     private IRemoteMagnificationAnimationCallback mAnimationCallback;
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
     private WindowMagnification mWindowMagnification;
@@ -79,7 +82,8 @@
         }).when(mAccessibilityManager).setWindowMagnificationConnection(
                 any(IWindowMagnificationConnection.class));
         mWindowMagnification = new WindowMagnification(getContext(),
-                getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController);
+                getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
+                mNavigationModeController);
         mWindowMagnification.mWindowMagnificationAnimationController =
                 mWindowMagnificationAnimationController;
         mWindowMagnification.requestWindowMagnificationConnection(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index c6440f4..5f2fd69 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import static android.view.Choreographer.FrameCallback;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import static org.hamcrest.Matchers.containsString;
@@ -28,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
@@ -294,4 +296,15 @@
         assertTrue(
                 mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
     }
+
+    @Test
+    public void onNavigationModeChanged_updateMirrorViewLayout() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+            mWindowMagnificationController.onNavigationModeChanged(NAV_BAR_MODE_GESTURAL);
+        });
+
+        verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 936558b..4a0e216 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -35,6 +35,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.statusbar.CommandQueue;
 
 import org.junit.Before;
@@ -53,6 +54,8 @@
     private AccessibilityManager mAccessibilityManager;
     @Mock
     private ModeSwitchesController mModeSwitchesController;
+    @Mock
+    private NavigationModeController mNavigationModeController;
     private CommandQueue mCommandQueue;
     private WindowMagnification mWindowMagnification;
 
@@ -63,7 +66,8 @@
 
         mCommandQueue = new CommandQueue(getContext());
         mWindowMagnification = new WindowMagnification(getContext(),
-                getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController);
+                getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController,
+                mNavigationModeController);
         mWindowMagnification.start();
     }
 
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 4bbc41e..6e2c7e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.bubbles;
 
-import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -29,7 +27,9 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
+import android.os.Bundle;
 import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -39,10 +39,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleData.TimeSource;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
 
 import com.google.common.collect.ImmutableList;
 
@@ -68,15 +64,15 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class BubbleDataTest extends SysuiTestCase {
 
-    private NotificationEntry mEntryA1;
-    private NotificationEntry mEntryA2;
-    private NotificationEntry mEntryA3;
-    private NotificationEntry mEntryB1;
-    private NotificationEntry mEntryB2;
-    private NotificationEntry mEntryB3;
-    private NotificationEntry mEntryC1;
-    private NotificationEntry mEntryInterruptive;
-    private NotificationEntry mEntryDismissed;
+    private BubbleEntry mEntryA1;
+    private BubbleEntry mEntryA2;
+    private BubbleEntry mEntryA3;
+    private BubbleEntry mEntryB1;
+    private BubbleEntry mEntryB2;
+    private BubbleEntry mEntryB3;
+    private BubbleEntry mEntryC1;
+    private BubbleEntry mEntryInterruptive;
+    private BubbleEntry mEntryDismissed;
 
     private Bubble mBubbleA1;
     private Bubble mBubbleA2;
@@ -99,8 +95,6 @@
     @Mock
     private PendingIntent mDeleteIntent;
 
-    private NotificationTestHelper mNotificationTestHelper;
-
     @Captor
     private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
 
@@ -112,29 +106,23 @@
 
     @Before
     public void setUp() throws Exception {
-        mNotificationTestHelper = new NotificationTestHelper(
-                mContext,
-                mDependency,
-                TestableLooper.get(this));
         MockitoAnnotations.initMocks(this);
 
-        mEntryA1 = createBubbleEntry(1, "a1", "package.a");
-        mEntryA2 = createBubbleEntry(1, "a2", "package.a");
-        mEntryA3 = createBubbleEntry(1, "a3", "package.a");
-        mEntryB1 = createBubbleEntry(1, "b1", "package.b");
-        mEntryB2 = createBubbleEntry(1, "b2", "package.b");
-        mEntryB3 = createBubbleEntry(1, "b3", "package.b");
-        mEntryC1 = createBubbleEntry(1, "c1", "package.c");
+        mEntryA1 = createBubbleEntry(1, "a1", "package.a", null);
+        mEntryA2 = createBubbleEntry(1, "a2", "package.a", null);
+        mEntryA3 = createBubbleEntry(1, "a3", "package.a", null);
+        mEntryB1 = createBubbleEntry(1, "b1", "package.b", null);
+        mEntryB2 = createBubbleEntry(1, "b2", "package.b", null);
+        mEntryB3 = createBubbleEntry(1, "b3", "package.b", null);
+        mEntryC1 = createBubbleEntry(1, "c1", "package.c", null);
 
-        mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d");
-        modifyRanking(mEntryInterruptive)
-                .setVisuallyInterruptive(true)
-                .build();
+        NotificationListenerService.Ranking ranking =
+                mock(NotificationListenerService.Ranking.class);
+        when(ranking.visuallyInterruptive()).thenReturn(true);
+        mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d", ranking);
         mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
 
-        ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
-        mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
-        mEntryDismissed.setRow(row);
+        mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d", null);
         mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
 
         mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
@@ -862,12 +850,13 @@
     }
 
 
-    private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
-        return createBubbleEntry(userId, notifKey, packageName, 1000);
+    private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
+            NotificationListenerService.Ranking ranking) {
+        return createBubbleEntry(userId, notifKey, packageName, ranking, 1000);
     }
 
-    private void setPostTime(NotificationEntry entry, long postTime) {
-        when(entry.getSbn().getPostTime()).thenReturn(postTime);
+    private void setPostTime(BubbleEntry entry, long postTime) {
+        when(entry.getStatusBarNotification().getPostTime()).thenReturn(postTime);
     }
 
     /**
@@ -875,16 +864,19 @@
      * required for BubbleData functionality and verification. NotificationTestHelper is used only
      * as a convenience to create a Notification w/BubbleMetadata.
      */
-    private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
-            long postTime) {
+    private BubbleEntry createBubbleEntry(int userId, String notifKey, String packageName,
+            NotificationListenerService.Ranking ranking, long postTime) {
         // BubbleMetadata
         Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder(
                 mExpandIntent, Icon.createWithResource("", 0))
                 .setDeleteIntent(mDeleteIntent)
                 .build();
         // Notification -> BubbleMetadata
-        Notification notification = mNotificationTestHelper.createNotification(false,
-                null /* groupKey */, bubbleMetadata);
+        Notification notification = mock(Notification.class);
+        notification.setBubbleMetadata(bubbleMetadata);
+
+        // Notification -> extras
+        notification.extras = new Bundle();
 
         // StatusBarNotification
         StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -895,18 +887,18 @@
         when(sbn.getNotification()).thenReturn(notification);
 
         // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
-        return new NotificationEntryBuilder().setSbn(sbn).build();
+        return new BubbleEntry(sbn, ranking, true, false, false, false);
     }
 
     private void setCurrentTime(long time) {
         when(mTimeSource.currentTimeMillis()).thenReturn(time);
     }
 
-    private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+    private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
         sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */);
     }
 
-    private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime,
+    private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime,
             boolean visuallyInterruptive) {
         setPostTime(entry, postTime);
         // BubbleController calls this:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index f7f3a37..29ead59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -24,12 +24,14 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -37,8 +39,6 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -52,8 +52,10 @@
 public class BubbleTest extends SysuiTestCase {
     @Mock
     private Notification mNotif;
+    @Mock
+    private StatusBarNotification mSbn;
 
-    private NotificationEntry mEntry;
+    private BubbleEntry mBubbleEntry;
     private Bundle mExtras;
     private Bubble mBubble;
 
@@ -67,18 +69,16 @@
         mExtras = new Bundle();
         mNotif.extras = mExtras;
 
-        mEntry = new NotificationEntryBuilder()
-                .setNotification(mNotif)
-                .build();
-
-        mBubble = new Bubble(mEntry, mSuppressionListener, null);
-
         Intent target = new Intent(mContext, BubblesTestActivity.class);
         Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
                 PendingIntent.getActivity(mContext, 0, target, 0),
                         Icon.createWithResource(mContext, R.drawable.android))
                 .build();
-        mEntry.setBubbleMetadata(metadata);
+        when(mSbn.getNotification()).thenReturn(mNotif);
+        when(mNotif.getBubbleMetadata()).thenReturn(metadata);
+        when(mSbn.getKey()).thenReturn("mock");
+        mBubbleEntry = new BubbleEntry(mSbn, null, true, false, false, false);
+        mBubble = new Bubble(mBubbleEntry, mSuppressionListener, null);
     }
 
     @Test
@@ -86,7 +86,7 @@
         final String msg = "Hello there!";
         doReturn(Notification.Style.class).when(mNotif).getNotificationStyle();
         mExtras.putCharSequence(Notification.EXTRA_TEXT, msg);
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -97,7 +97,7 @@
         mExtras.putCharSequence(Notification.EXTRA_BIG_TEXT, msg);
 
         // Should be big text, not the small text.
-        assertEquals(msg, BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertEquals(msg, Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -105,7 +105,7 @@
         doReturn(Notification.MediaStyle.class).when(mNotif).getNotificationStyle();
 
         // Media notifs don't get update messages.
-        assertNull(BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+        assertNull(Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -121,7 +121,7 @@
 
         // Should be the last one only.
         assertEquals("Really? I prefer them that way.",
-                BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
+                Bubble.extractFlyoutMessage(mBubbleEntry).message);
     }
 
     @Test
@@ -136,8 +136,8 @@
                                 "Oh, hello!", 0, "Mady").toBundle()});
 
         // Should be the last one only.
-        assertEquals("Oh, hello!", BubbleViewInfoTask.extractFlyoutMessage(mEntry).message);
-        assertEquals("Mady", BubbleViewInfoTask.extractFlyoutMessage(mEntry).senderName);
+        assertEquals("Oh, hello!", Bubble.extractFlyoutMessage(mBubbleEntry).message);
+        assertEquals("Mady", Bubble.extractFlyoutMessage(mBubbleEntry).senderName);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index ca328fb..9ebb587 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.phone.ShadeController;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -73,6 +74,11 @@
         when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
     }
 
+    @After
+    public void tearDown() {
+        mMediaOutputDialog.dismissDialog();
+    }
+
     @Test
     public void getStopButtonVisibility_remoteDevice_returnVisible() {
         mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 1c2d0840..e472cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -37,7 +37,6 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
-import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -116,11 +115,6 @@
 
         qs.setListening(false);
         processAllMessages();
-
-        // Manually push header through detach so it can handle standard cleanup it does on
-        // removed from window.
-        ((QuickStatusBarHeader) qs.getView().findViewById(R.id.header)).onDetachedFromWindow();
-
         host.destroy();
         processAllMessages();
     }
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 915c2f6..613d28b 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -102,6 +102,7 @@
     ],
     libs: [
         "framework-tethering",
+        "framework-wifi",
     ],
     jarjar_rules: "jarjar-rules.txt",
     optimize: {
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index fd9e360..b285849 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -15,6 +15,7 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.TetheringManager.TETHERING_WIFI_P2P;
 
 import static java.util.Arrays.asList;
@@ -23,7 +24,6 @@
 import android.net.ConnectivityManager;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.net.Network;
 import android.net.ip.IpServer;
 import android.net.util.PrefixUtils;
@@ -90,16 +90,24 @@
 
     /**
      * Record a new upstream IpPrefix which may conflict with tethering downstreams.
-     * The downstreams will be notified if a conflict is found.
+     * The downstreams will be notified if a conflict is found. When updateUpstreamPrefix is called,
+     * UpstreamNetworkState must have an already populated LinkProperties.
      */
-    public void updateUpstreamPrefix(final Network network, final LinkProperties lp) {
-        final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(lp.getAllLinkAddresses());
-        if (ipv4Prefixes.isEmpty()) {
-            removeUpstreamPrefix(network);
+    public void updateUpstreamPrefix(final UpstreamNetworkState ns) {
+        // Do not support VPN as upstream
+        if (ns.networkCapabilities != null && ns.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+            removeUpstreamPrefix(ns.network);
             return;
         }
 
-        mUpstreamPrefixMap.put(network, ipv4Prefixes);
+        final ArrayList<IpPrefix> ipv4Prefixes = getIpv4Prefixes(
+                ns.linkProperties.getAllLinkAddresses());
+        if (ipv4Prefixes.isEmpty()) {
+            removeUpstreamPrefix(ns.network);
+            return;
+        }
+
+        mUpstreamPrefixMap.put(ns.network, ipv4Prefixes);
         handleMaybePrefixConflict(ipv4Prefixes);
     }
 
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 64d5025..474f4e8 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1678,14 +1678,6 @@
             }
         }
 
-        private void addUpstreamPrefixes(final UpstreamNetworkState ns) {
-            mPrivateAddressCoordinator.updateUpstreamPrefix(ns.network, ns.linkProperties);
-        }
-
-        private void removeUpstreamPrefixes(final UpstreamNetworkState ns) {
-            mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
-        }
-
         @VisibleForTesting
         void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
             if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
@@ -1696,10 +1688,10 @@
             final UpstreamNetworkState ns = (UpstreamNetworkState) o;
             switch (arg1) {
                 case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
-                    addUpstreamPrefixes(ns);
+                    mPrivateAddressCoordinator.updateUpstreamPrefix(ns);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LOST:
-                    removeUpstreamPrefixes(ns);
+                    mPrivateAddressCoordinator.removeUpstreamPrefix(ns.network);
                     break;
             }
 
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 8e93c2e..7b6632c 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
@@ -15,6 +15,10 @@
  */
 package com.android.networkstack.tethering;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
 import static android.net.TetheringManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_WIFI;
@@ -30,13 +34,12 @@
 
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.ip.IpServer;
-import android.net.util.NetworkConstants;
 import android.net.util.PrefixUtils;
 
 import androidx.test.filters.SmallTest;
@@ -48,13 +51,10 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public final class PrivateAddressCoordinatorTest {
-    private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
-    private static final String TEST_WIFI_IFNAME = "test_wlan0";
+    private static final String TEST_IFNAME = "test0";
 
     @Mock private IpServer mHotspotIpServer;
     @Mock private IpServer mUsbIpServer;
@@ -69,7 +69,8 @@
     private final LinkAddress mLegacyWifiP2pAddress = new LinkAddress("192.168.49.1/24");
     private final Network mWifiNetwork = new Network(1);
     private final Network mMobileNetwork = new Network(2);
-    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork};
+    private final Network mVpnNetwork = new Network(3);
+    private final Network[] mAllNetworks = {mMobileNetwork, mWifiNetwork, mVpnNetwork};
 
     private void setUpIpServers() throws Exception {
         when(mUsbIpServer.interfaceType()).thenReturn(TETHERING_USB);
@@ -184,33 +185,25 @@
         assertEquals("Fail to reselect available prefix: ", predefinedPrefix, allowUseFreePrefix);
     }
 
-    private LinkProperties buildUpstreamLinkProperties(boolean withIPv4, boolean withIPv6,
-            boolean isMobile) {
-        final String testIface;
-        final String testIpv4Address;
-        if (isMobile) {
-            testIface = TEST_MOBILE_IFNAME;
-            testIpv4Address = "10.0.0.1";
-        } else {
-            testIface = TEST_WIFI_IFNAME;
-            testIpv4Address = "192.168.43.5";
-        }
-
+    private UpstreamNetworkState buildUpstreamNetworkState(final Network network,
+            final LinkAddress v4Addr, final LinkAddress v6Addr, final NetworkCapabilities cap) {
         final LinkProperties prop = new LinkProperties();
-        prop.setInterfaceName(testIface);
+        prop.setInterfaceName(TEST_IFNAME);
+        if (v4Addr != null) prop.addLinkAddress(v4Addr);
 
-        if (withIPv4) {
-            prop.addLinkAddress(
-                    new LinkAddress(InetAddresses.parseNumericAddress(testIpv4Address),
-                            NetworkConstants.IPV4_ADDR_BITS));
+        if (v6Addr != null) prop.addLinkAddress(v6Addr);
+
+        return new UpstreamNetworkState(prop, cap, network);
+    }
+
+    private NetworkCapabilities makeNetworkCapabilities(final int transportType) {
+        final NetworkCapabilities cap = new NetworkCapabilities();
+        cap.addTransportType(transportType);
+        if (transportType == TRANSPORT_VPN) {
+            cap.removeCapability(NET_CAPABILITY_NOT_VPN);
         }
 
-        if (withIPv6) {
-            prop.addLinkAddress(
-                    new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
-                            NetworkConstants.RFC7421_PREFIX_LENGTH));
-        }
-        return prop;
+        return cap;
     }
 
     @Test
@@ -220,53 +213,76 @@
         final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24");
         // Force always get subAddress "43.5" for conflict testing.
         when(mPrivateAddressCoordinator.getRandomSubAddr()).thenReturn(fakeHotspotSubAddr);
-        // 1. Enable hotspot with prefix 192.168.43.0/24
+        // - Enable hotspot with prefix 192.168.43.0/24
         final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer);
         final IpPrefix hotspotPrefix = PrefixUtils.asIpPrefix(hotspotAddr);
         assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix);
         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr);
-        // 2. Update v6 only mobile network, hotspot prefix should not be removed.
-        List<String> testConflicts;
-        final LinkProperties v6OnlyMobileProp = buildUpstreamLinkProperties(false, true, true);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v6OnlyMobileProp);
+        // - test mobile network with null NetworkCapabilities. Ideally this should not happen,
+        // just make sure no crash in this case.
+        final UpstreamNetworkState noCapUpstream = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("10.0.0.8/24"), null, null);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // - test mobile upstream with no address.
+        final UpstreamNetworkState noAddress = buildUpstreamNetworkState(mMobileNetwork,
+                null, null, makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(noCapUpstream);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // - Update v6 only mobile network, hotspot prefix should not be removed.
+        final UpstreamNetworkState v6OnlyMobile = buildUpstreamNetworkState(mMobileNetwork,
+                null, new LinkAddress("2001:db8::/64"),
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyMobile);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
         mPrivateAddressCoordinator.removeUpstreamPrefix(mMobileNetwork);
-        // 3. Update v4 only mobile network, hotspot prefix should not be removed.
-        final LinkProperties v4OnlyMobileProp = buildUpstreamLinkProperties(true, false, true);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4OnlyMobileProp);
+        // - Update v4 only mobile network, hotspot prefix should not be removed.
+        final UpstreamNetworkState v4OnlyMobile = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("10.0.0.8/24"), null,
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyMobile);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
-        // 4. Update v4v6 mobile network, hotspot prefix should not be removed.
-        final LinkProperties v4v6MobileProp = buildUpstreamLinkProperties(true, true, true);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mMobileNetwork, v4v6MobileProp);
+        // - Update v4v6 mobile network, hotspot prefix should not be removed.
+        final UpstreamNetworkState v4v6Mobile = buildUpstreamNetworkState(mMobileNetwork,
+                new LinkAddress("10.0.0.8/24"), new LinkAddress("2001:db8::/64"),
+                makeNetworkCapabilities(TRANSPORT_CELLULAR));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v4v6Mobile);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
-        // 5. Update v6 only wifi network, hotspot prefix should not be removed.
-        final LinkProperties v6OnlyWifiProp = buildUpstreamLinkProperties(false, true, false);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v6OnlyWifiProp);
+        // - Update v6 only wifi network, hotspot prefix should not be removed.
+        final UpstreamNetworkState v6OnlyWifi = buildUpstreamNetworkState(mWifiNetwork,
+                null, new LinkAddress("2001:db8::/64"), makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v6OnlyWifi);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
-        // 6. Update v4 only wifi network, it conflict with hotspot prefix.
-        final LinkProperties v4OnlyWifiProp = buildUpstreamLinkProperties(true, false, false);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+        // - Update vpn network, it conflict with hotspot prefix but VPN networks are ignored.
+        final UpstreamNetworkState v4OnlyVpn = buildUpstreamNetworkState(mVpnNetwork,
+                new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_VPN));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyVpn);
+        verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
+        // - Update v4 only wifi network, it conflict with hotspot prefix.
+        final UpstreamNetworkState v4OnlyWifi = buildUpstreamNetworkState(mWifiNetwork,
+                new LinkAddress("192.168.43.5/24"), null, makeNetworkCapabilities(TRANSPORT_WIFI));
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
         verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
         reset(mHotspotIpServer);
-        // 7. Restart hotspot again and its prefix is different previous.
+        // - Restart hotspot again and its prefix is different previous.
         mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer);
         final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mHotspotIpServer);
         final IpPrefix hotspotPrefix2 = PrefixUtils.asIpPrefix(hotspotAddr2);
         assertNotEquals(hotspotPrefix, hotspotPrefix2);
         when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2);
-        mPrivateAddressCoordinator.updateUpstreamPrefix(mWifiNetwork, v4OnlyWifiProp);
+        mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi);
         verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT);
-        // 7. Usb tethering can be enabled and its prefix is different with conflict one.
+        // - Usb tethering can be enabled and its prefix is different with conflict one.
         final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mUsbIpServer);
         final IpPrefix usbPrefix = PrefixUtils.asIpPrefix(usbAddr);
         assertNotEquals(predefinedPrefix, usbPrefix);
         assertNotEquals(hotspotPrefix2, usbPrefix);
         when(mUsbIpServer.getAddress()).thenReturn(usbAddr);
-        // 8. Disable wifi upstream, then wifi's prefix can be selected again.
+        // - Disable wifi upstream, then wifi's prefix can be selected again.
         mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork);
         final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress(
                 mEthernetIpServer);
diff --git a/services/Android.bp b/services/Android.bp
index ef52c2a..25a0d7e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -3,6 +3,12 @@
     plugins: [
         "error_prone_android_framework",
     ],
+    errorprone: {
+        javacflags: [
+            "-Xep:AndroidFrameworkCompatChange:ERROR",
+            "-Xep:AndroidFrameworkUid:ERROR",
+        ],
+    },
 }
 
 filegroup {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 6ca99d1..40b1718 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -317,7 +317,7 @@
                     AndroidFuture<Association> future = new AndroidFuture<>();
                     service.startDiscovery(request, callingPackage, callback, future);
                     return future;
-                }).whenComplete(uncheckExceptions((association, err) -> {
+                }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> {
                     if (err == null) {
                         addAssociation(association);
                     } else {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 6d77486..7775354 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -18,14 +18,10 @@
 
 import static android.Manifest.permission.ACCESS_MTP;
 import static android.Manifest.permission.INSTALL_PACKAGES;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
 import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -137,7 +133,6 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.AppFuseMount;
@@ -153,7 +148,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.Installer;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
@@ -2523,19 +2517,6 @@
         abortIdleMaint(null);
     }
 
-    private void remountUidExternalStorage(int uid, int mode) {
-        if (uid == Process.SYSTEM_UID) {
-            // No need to remount uid for system because it has all access anyways
-            return;
-        }
-
-        try {
-            mVold.remountUid(uid, mode);
-        } catch (Exception e) {
-            Slog.wtf(TAG, e);
-        }
-    }
-
     @Override
     public void setDebugFlags(int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -3847,21 +3828,6 @@
         }
     }
 
-    private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() {
-        @Override
-        public void opChanged(int op, int uid, String packageName) throws RemoteException {
-            if (!ENABLE_ISOLATED_STORAGE) return;
-
-            int mountMode = getMountMode(uid, packageName);
-            boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class)
-                    .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT;
-
-            if (isUidActive) {
-                remountUidExternalStorage(uid, mountMode);
-            }
-        }
-    };
-
     private void addObbStateLocked(ObbState obbState) throws RemoteException {
         final IBinder binder = obbState.getBinder();
         List<ObbState> obbStates = mObbMounts.get(binder);
@@ -4236,19 +4202,9 @@
             }
 
             // Determine if caller is holding runtime permission
-            final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
-                    uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE);
             final boolean hasWrite = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
                     uid, packageName, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE);
 
-            // We're only willing to give out broad access if they also hold
-            // runtime permission; this is a firm CDD requirement
-            final boolean hasFull = mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE,
-                    uid) == PERMISSION_GRANTED;
-            if (hasFull && hasWrite) {
-                return Zygote.MOUNT_EXTERNAL_FULL;
-            }
-
             // We're only willing to give out installer access if they also hold
             // runtime permission; this is a firm CDD requirement
             final boolean hasInstall = mIPackageManager.checkUidPermission(INSTALL_PACKAGES,
@@ -4268,19 +4224,7 @@
             if ((hasInstall || hasInstallOp) && hasWrite) {
                 return Zygote.MOUNT_EXTERNAL_INSTALLER;
             }
-
-            // Otherwise we're willing to give out sandboxed or non-sandboxed if
-            // they hold the runtime permission
-            boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
-                    uid, packageName) == MODE_ALLOWED;
-
-            if (hasLegacy && hasWrite) {
-                return Zygote.MOUNT_EXTERNAL_WRITE;
-            } else if (hasLegacy && hasRead) {
-                return Zygote.MOUNT_EXTERNAL_READ;
-            } else {
-                return Zygote.MOUNT_EXTERNAL_DEFAULT;
-            }
+            return Zygote.MOUNT_EXTERNAL_DEFAULT;
         } catch (RemoteException e) {
             // Should not happen
         }
@@ -4570,12 +4514,6 @@
         }
 
         @Override
-        public void onExternalStoragePolicyChanged(int uid, String packageName) {
-            final int mountMode = getExternalStorageMountMode(uid, packageName);
-            remountUidExternalStorage(uid, mountMode);
-        }
-
-        @Override
         public int getExternalStorageMountMode(int uid, String packageName) {
             if (ENABLE_ISOLATED_STORAGE) {
                 return getMountMode(uid, packageName);
@@ -4720,16 +4658,6 @@
                         updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
                         return;
                 }
-
-                if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE
-                                || code == OP_WRITE_EXTERNAL_STORAGE
-                                || code == OP_REQUEST_INSTALL_PACKAGES)) {
-                    final UserManagerInternal userManagerInternal =
-                            LocalServices.getService(UserManagerInternal.class);
-                    if (userManagerInternal.isUserInitialized(UserHandle.getUserId(uid))) {
-                        onExternalStoragePolicyChanged(uid, packageName);
-                    }
-                }
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5a34283..aaff831 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -42,7 +42,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
@@ -5736,8 +5735,8 @@
     private boolean isProfileOwner(int uid) {
         final DevicePolicyManagerInternal dpmi =
                 LocalServices.getService(DevicePolicyManagerInternal.class);
-        return (dpmi != null)
-                && dpmi.isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
+        return (dpmi != null) && (dpmi.isActiveProfileOwner(uid) || dpmi.isActiveDeviceOwner(uid));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ffdcd15..1fe0012 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -153,7 +153,6 @@
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
-import android.app.IAssistDataReceiver;
 import android.app.IInstrumentationWatcher;
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
@@ -286,7 +285,6 @@
 import android.util.proto.ProtoUtils;
 import android.view.Display;
 import android.view.Gravity;
-import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
@@ -2397,8 +2395,8 @@
     }
 
     @Override
-    public void setFocusedStack(int stackId) {
-        mActivityTaskManager.setFocusedStack(stackId);
+    public void setFocusedRootTask(int taskId) {
+        mActivityTaskManager.setFocusedRootTask(taskId);
     }
 
     /** Sets the task stack listener that gets callbacks when a task stack changes. */
@@ -2840,18 +2838,6 @@
         return mActivityTaskManager.startActivityFromRecents(taskId, bOptions);
     }
 
-    @Override
-    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
-            IRecentsAnimationRunner recentsAnimationRunner) {
-        mActivityTaskManager.startRecentsActivity(
-                intent, assistDataReceiver, recentsAnimationRunner);
-    }
-
-    @Override
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-        mActivityTaskManager.cancelRecentsAnimation(restoreHomeStackPosition);
-    }
-
     /**
      * This is the internal entry point for handling Activity.finish().
      *
@@ -5672,11 +5658,6 @@
     }
 
     @Override
-    public void removeStack(int stackId) {
-        mActivityTaskManager.removeStack(stackId);
-    }
-
-    @Override
     public boolean removeTask(int taskId) {
         return mActivityTaskManager.removeTask(taskId);
     }
@@ -5712,8 +5693,8 @@
     }
 
     @Override
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        mActivityTaskManager.moveTaskToStack(taskId, stackId, toTop);
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
+        mActivityTaskManager.moveTaskToRootTask(taskId, rootTaskId, toTop);
     }
 
     @Override
@@ -5723,17 +5704,17 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
      *
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds Bounds to use for pinned stack.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds Bounds to use for pinned root task.
      *
-     * @return True if the top activity of the input stack was successfully moved to the pinned
-     *          stack.
+     * @return True if the top activity of the input root task was successfully moved to the pinned
+     *          root task.
      */
     @Override
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
-        return mActivityTaskManager.moveTopActivityToPinnedStack(stackId, bounds);
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
+        return mActivityTaskManager.moveTopActivityToPinnedRootTask(rootTaskId, bounds);
     }
 
     @Override
@@ -8198,12 +8179,10 @@
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-        if (uid == Process.INVALID_UID) {
-            return Process.INVALID_UID;
-        }
+        // If the uid is Process.INVALID_UID, the below 'if' check will be always true
         if (UserHandle.getAppId(uid) != UserHandle.getAppId(callingUid)) {
             // Requires the DUMP permission if the target package doesn't belong
-            // to the caller.
+            // to the caller or it doesn't exist.
             enforceCallingPermission(android.Manifest.permission.DUMP, function);
         }
         return uid;
@@ -16492,7 +16471,7 @@
         @Override
         public int getStorageMountMode(int pid, int uid) {
             if (uid == SHELL_UID || uid == ROOT_UID) {
-                return Zygote.MOUNT_EXTERNAL_FULL;
+                return Zygote.MOUNT_EXTERNAL_DEFAULT;
             }
             synchronized (mPidsSelfLocked) {
                 final ProcessRecord pr = mPidsSelfLocked.get(pid);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d346430..31e7106 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2587,9 +2587,9 @@
             case "info":
                 return runRootTaskInfo(pw);
             case "move-top-activity-to-pinned-stack":
-                return runMoveTopActivityToPinnedStack(pw);
+                return runMoveTopActivityToPinnedRootTask(pw);
             case "remove":
-                return runStackRemove(pw);
+                return runRootTaskRemove(pw);
             default:
                 getErrPrintWriter().println("Error: unknown command '" + op + "'");
                 return -1;
@@ -2626,19 +2626,19 @@
     }
 
     int runDisplayMoveStack(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
+        String rootTaskIdStr = getNextArgRequired();
+        int rootTaskId = Integer.parseInt(rootTaskIdStr);
         String displayIdStr = getNextArgRequired();
         int displayId = Integer.parseInt(displayIdStr);
-        mTaskInterface.moveStackToDisplay(stackId, displayId);
+        mTaskInterface.moveRootTaskToDisplay(rootTaskId, displayId);
         return 0;
     }
 
     int runStackMoveTask(PrintWriter pw) throws RemoteException {
         String taskIdStr = getNextArgRequired();
         int taskId = Integer.parseInt(taskIdStr);
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
+        String rootTaskIdStr = getNextArgRequired();
+        int rootTaskId = Integer.parseInt(rootTaskIdStr);
         String toTopStr = getNextArgRequired();
         final boolean toTop;
         if ("true".equals(toTopStr)) {
@@ -2650,7 +2650,7 @@
             return -1;
         }
 
-        mTaskInterface.moveTaskToStack(taskId, stackId, toTop);
+        mTaskInterface.moveTaskToRootTask(taskId, rootTaskId, toTop);
         return 0;
     }
 
@@ -2670,22 +2670,22 @@
         return 0;
     }
 
-    int runStackRemove(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
-        mTaskInterface.removeStack(stackId);
+    int runRootTaskRemove(PrintWriter pw) throws RemoteException {
+        String taskIdStr = getNextArgRequired();
+        int taskId = Integer.parseInt(taskIdStr);
+        mTaskInterface.removeTask(taskId);
         return 0;
     }
 
-    int runMoveTopActivityToPinnedStack(PrintWriter pw) throws RemoteException {
-        int stackId = Integer.parseInt(getNextArgRequired());
+    int runMoveTopActivityToPinnedRootTask(PrintWriter pw) throws RemoteException {
+        int rootTaskId = Integer.parseInt(getNextArgRequired());
         final Rect bounds = getBounds();
         if (bounds == null) {
             getErrPrintWriter().println("Error: invalid input bounds");
             return -1;
         }
 
-        if (!mTaskInterface.moveTopActivityToPinnedStack(stackId, bounds)) {
+        if (!mTaskInterface.moveTopActivityToPinnedRootTask(rootTaskId, bounds)) {
             getErrPrintWriter().println("Didn't move top activity to pinned stack.");
             return -1;
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5e65563..ed47616d9 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -92,7 +92,6 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.system.Os;
 import android.text.TextUtils;
@@ -1780,14 +1779,10 @@
                     final IPackageManager pm = AppGlobals.getPackageManager();
                     permGids = pm.getPackageGids(app.info.packageName,
                             MATCH_DIRECT_BOOT_AUTO, app.userId);
-                    if (StorageManager.hasIsolatedStorage() && mountExtStorageFull) {
-                        mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
-                    } else {
-                        StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                                StorageManagerInternal.class);
-                        mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
-                                app.info.packageName);
-                    }
+                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
+                            StorageManagerInternal.class);
+                    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
+                            app.info.packageName);
                 } catch (RemoteException e) {
                     throw e.rethrowAsRuntimeException();
                 }
@@ -1918,7 +1913,9 @@
 
             String instructionSet = null;
             if (app.info.primaryCpuAbi != null) {
-                instructionSet = VMRuntime.getInstructionSet(app.info.primaryCpuAbi);
+                // If ABI override is specified, use the isa derived from the value of ABI override.
+                // Otherwise, use the isa derived from primary ABI
+                instructionSet = VMRuntime.getInstructionSet(requiredAbi);
             }
 
             app.gids = gids;
@@ -1927,7 +1924,7 @@
 
             // If instructionSet is non-null, this indicates that the system_server is spawning a
             // process with an ISA that may be different from its own. System (kernel and hardware)
-            // compatililty for these features is checked in the decideTaggingLevel in the
+            // compatibility for these features is checked in the decideTaggingLevel in the
             // system_server process (not the child process). As both MTE and TBI are only supported
             // in aarch64, we can simply ensure that the new process is also aarch64. This prevents
             // the mismatch where a 64-bit system server spawns a 32-bit child that thinks it should
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index c1777b8..7dbb39e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1709,24 +1709,13 @@
                             if (Process.isIsolated(uid)) {
                                 return Zygote.MOUNT_EXTERNAL_NONE;
                             }
-                            if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
-                                    packageName, null, true, "External storage policy", true)
-                                    != AppOpsManager.MODE_ALLOWED) {
-                                return Zygote.MOUNT_EXTERNAL_NONE;
-                            }
-                            if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
-                                    packageName, null, true, "External storage policy", true)
-                                    != AppOpsManager.MODE_ALLOWED) {
-                                return Zygote.MOUNT_EXTERNAL_READ;
-                            }
-                            return Zygote.MOUNT_EXTERNAL_WRITE;
+                            return Zygote.MOUNT_EXTERNAL_DEFAULT;
                         }
 
                         @Override
                         public boolean hasExternalStorage(int uid, String packageName) {
                             final int mountMode = getMountMode(uid, packageName);
-                            return mountMode == Zygote.MOUNT_EXTERNAL_READ
-                                    || mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
+                            return mountMode != Zygote.MOUNT_EXTERNAL_NONE;
                         }
                     });
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 92c498c..bac944f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -27,8 +27,6 @@
 
     private static final String TAG = "GenerateChallengeClient";
 
-    protected long mChallenge;
-
     public GenerateChallengeClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
             @NonNull String owner, int sensorId) {
@@ -51,12 +49,5 @@
         super.start(callback);
 
         startHalOperation();
-        try {
-            getListener().onChallengeGenerated(getSensorId(), mChallenge);
-            mCallback.onClientFinished(this, true /* success */);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Remote exception", e);
-            mCallback.onClientFinished(this, false /* success */);
-        }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
index ba401f2..406a7cc 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -59,7 +59,14 @@
     @Override
     protected void startHalOperation() {
         try {
-            mChallenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+            final long challenge = getFreshDaemon().generateChallenge(CHALLENGE_TIMEOUT_SEC).value;
+            try {
+                getListener().onChallengeGenerated(getSensorId(), challenge);
+                mCallback.onClientFinished(this, true /* success */);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+                mCallback.onClientFinished(this, false /* success */);
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "generateChallenge failed", e);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index abaaac5..5169c7d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -45,7 +45,14 @@
     @Override
     protected void startHalOperation() {
         try {
-            mChallenge = getFreshDaemon().preEnroll();
+            final long challenge = getFreshDaemon().preEnroll();
+            try {
+                getListener().onChallengeGenerated(getSensorId(), challenge);
+                mCallback.onClientFinished(this, true /* success */);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote exception", e);
+                mCallback.onClientFinished(this, false /* success */);
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "preEnroll failed", e);
         }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 14b3478..a45d94b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1130,7 +1130,13 @@
         return mNetworkInfo;
     }
 
-    public int getNetId() {
+    /**
+     * Return netId of current running VPN network.
+     *
+     * @return a netId if there is a running VPN network or NETID_UNSET if there is no running VPN
+     *         network or network is null.
+     */
+    public synchronized int getNetId() {
         final NetworkAgent agent = mNetworkAgent;
         if (null == agent) return NETID_UNSET;
         final Network network = agent.getNetwork();
@@ -1708,7 +1714,7 @@
     /**
      * Return the configuration of the currently running VPN.
      */
-    public VpnConfig getVpnConfig() {
+    public synchronized VpnConfig getVpnConfig() {
         enforceControlPermission();
         return mConfig;
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index bb6f14c..96679c3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -16,6 +16,8 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiPortInfo;
 import android.hardware.tv.cec.V1_0.CecMessage;
 import android.hardware.tv.cec.V1_0.HotplugEvent;
@@ -774,6 +776,7 @@
         private IHdmiCec mHdmiCec;
         private final Object mLock = new Object();
         private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+        @Nullable private HdmiCecCallback mCallback;
 
         @Override
         public String nativeInit() {
@@ -782,7 +785,7 @@
 
         boolean connectToHal() {
             try {
-                mHdmiCec = IHdmiCec.getService();
+                mHdmiCec = IHdmiCec.getService(true);
                 try {
                     mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
                 } catch (RemoteException e) {
@@ -796,7 +799,8 @@
         }
 
         @Override
-        public void setCallback(HdmiCecCallback callback) {
+        public void setCallback(@NonNull HdmiCecCallback callback) {
+            mCallback = callback;
             try {
                 mHdmiCec.setCallback(callback);
             } catch (RemoteException e) {
@@ -936,6 +940,10 @@
             if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
                 HdmiLogger.error("Service died cookie : " + cookie + "; reconnecting");
                 connectToHal();
+                // Reconnect the callback
+                if (mCallback != null) {
+                    setCallback(mCallback);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index cc8a330..a2304f4 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -231,6 +231,8 @@
     private static native void nativePilferPointers(long ptr, IBinder token);
     private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
     private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
+    private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity);
+    private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode);
     private static native int nativeInjectInputEvent(long ptr, InputEvent event,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
@@ -399,6 +401,8 @@
         registerShowTouchesSettingObserver();
         registerAccessibilityLargePointerSettingObserver();
         registerLongPressTimeoutObserver();
+        registerMaximumObscuringOpacityForTouchSettingObserver();
+        registerBlockUntrustedTouchesModeSettingObserver();
 
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
@@ -414,6 +418,8 @@
         updateShowTouchesFromSettings();
         updateAccessibilityLargePointerFromSettings();
         updateDeepPressStatusFromSettings("just booted");
+        updateMaximumObscuringOpacityForTouchFromSettings();
+        updateBlockUntrustedTouchesModeFromSettings();
     }
 
     // TODO(BT) Pass in parameter for bluetooth system
@@ -1739,6 +1745,46 @@
                 }, UserHandle.USER_ALL);
     }
 
+    private void registerBlockUntrustedTouchesModeSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE),
+                /* notifyForDescendants */ true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateBlockUntrustedTouchesModeFromSettings();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void updateBlockUntrustedTouchesModeFromSettings() {
+        final int mode = InputManager.getInstance().getBlockUntrustedTouchesMode(mContext);
+        nativeSetBlockUntrustedTouchesMode(mPtr, mode);
+    }
+
+    private void registerMaximumObscuringOpacityForTouchSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH),
+                /* notifyForDescendants */ true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateMaximumObscuringOpacityForTouchFromSettings();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void updateMaximumObscuringOpacityForTouchFromSettings() {
+        final float opacity = InputManager.getInstance().getMaximumObscuringOpacityForTouch(
+                mContext);
+        if (opacity < 0 || opacity > 1) {
+            Log.e(TAG, "Invalid maximum obscuring opacity " + opacity
+                    + ", it should be >= 0 and <= 1, rejecting update.");
+            return;
+        }
+        nativeSetMaximumObscuringOpacityForTouch(mPtr, opacity);
+    }
+
     private int getShowTouchesSetting(int defaultValue) {
         int result = defaultValue;
         try {
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index 6f35c8b..ecdd429 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -111,7 +111,7 @@
      */
     public Location createCoarse(Location fine) {
         synchronized (this) {
-            if (fine == mCachedFineLocation) {
+            if (fine == mCachedFineLocation || fine == mCachedCoarseLocation) {
                 return mCachedCoarseLocation;
             }
         }
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 227cdee..f72fee6 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,12 +17,14 @@
 package com.android.server.location;
 
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.compat.CompatChanges.isChangeEnabled;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
 import static android.location.LocationManager.NETWORK_PROVIDER;
+import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE;
 import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
 
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -75,7 +77,6 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ProviderProperties;
@@ -570,7 +571,7 @@
                     new IllegalArgumentException());
         }
 
-        request = validateLocationRequest(request);
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -592,7 +593,21 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLocationRequest(request);
+        // pending intents requests may not use system apis because we do not keep track if clients
+        // lose the relevant permissions, and thus should not get the benefit of those apis. its
+        // simplest to ensure these apis are simply never set for pending intent requests. the same
+        // does not apply for listener requests since those will have the process (including the
+        // listener) killed on permission removal
+        boolean usesSystemApi = request.isLowPower()
+                || request.isHiddenFromAppOps()
+                || request.isLocationSettingsIgnored()
+                || !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");
+        }
+
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -601,9 +616,9 @@
         manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
     }
 
-    private LocationRequest validateLocationRequest(LocationRequest request) {
-        WorkSource workSource = request.getWorkSource();
-        if (workSource != null && !workSource.isEmpty()) {
+    private LocationRequest validateLocationRequest(LocationRequest request,
+            CallerIdentity identity) {
+        if (!request.getWorkSource().isEmpty()) {
             mContext.enforceCallingOrSelfPermission(
                     permission.UPDATE_DEVICE_STATS,
                     "setting a work source requires " + permission.UPDATE_DEVICE_STATS);
@@ -634,23 +649,25 @@
             }
         }
 
-        if (request.getWorkSource() != null) {
-            if (request.getWorkSource().isEmpty()) {
-                sanitized.setWorkSource(null);
-            } else if (request.getWorkSource().getPackageName(0) == null) {
-                Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
-                sanitized.setWorkSource(null);
-            } else {
-                List<WorkChain> workChains = request.getWorkSource().getWorkChains();
-                if (workChains != null && !workChains.isEmpty() && workChains.get(
-                        0).getAttributionTag() == null) {
-                    Log.w(TAG,
-                            "received (and ignoring) illegal worksource with no attribution tag");
-                    sanitized.setWorkSource(null);
-                }
+        WorkSource workSource = new WorkSource(request.getWorkSource());
+        if (workSource.size() > 0 && workSource.getPackageName(0) == null) {
+            Log.w(TAG, "received (and ignoring) illegal worksource with no package name");
+            workSource.clear();
+        } else {
+            List<WorkChain> workChains = workSource.getWorkChains();
+            if (workChains != null && !workChains.isEmpty()
+                    && workChains.get(0).getAttributionTag() == null) {
+                Log.w(TAG,
+                        "received (and ignoring) illegal worksource with no attribution tag");
+                workSource.clear();
             }
         }
 
+        if (workSource.isEmpty()) {
+            identity.addToWorkSource(workSource);
+        }
+        sanitized.setWorkSource(workSource);
+
         return sanitized.build();
     }
 
@@ -684,15 +701,7 @@
             return null;
         }
 
-        Location location = manager.getLastLocation(identity, permissionLevel, false);
-
-        // lastly - note app ops
-        if (!mInjector.getAppOpsHelper().noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
-                identity)) {
-            return null;
-        }
-
-        return location;
+        return manager.getLastLocation(identity, permissionLevel, false);
     }
 
     @Nullable
@@ -710,7 +719,7 @@
         // clients in the system process must have an attribution tag set
         Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
 
-        request = validateLocationRequest(request);
+        request = validateLocationRequest(request, identity);
 
         LocationProviderManager manager = getLocationProviderManager(provider);
         Preconditions.checkArgument(manager != null,
@@ -727,7 +736,6 @@
                 return null;
             }
 
-            // use fine permission level to avoid creating unnecessary coarse locations
             Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
                     PERMISSION_FINE, false, Long.MAX_VALUE);
             if (location == null) {
@@ -1116,9 +1124,8 @@
             return;
         }
 
-        ipw.print("Location Manager State:");
+        ipw.println("Location Manager State:");
         ipw.increaseIndent();
-        ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
 
         ipw.println("User Info:");
         ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 48f8da4..ecf82e8 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -31,6 +31,7 @@
 import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
 
 import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
+import static com.android.internal.location.ProviderRequest.INTERVAL_DISABLED;
 import static com.android.server.location.LocationManagerService.D;
 import static com.android.server.location.LocationManagerService.TAG;
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -238,8 +239,7 @@
     protected abstract class Registration extends RemoteListenerRegistration<LocationRequest,
             LocationTransport, LocationListenerOperation> {
 
-        @PermissionLevel protected final int mPermissionLevel;
-        private final WorkSource mWorkSource;
+        private final @PermissionLevel int mPermissionLevel;
 
         // we cache these values because checking/calculating on the fly is more expensive
         private boolean mPermitted;
@@ -247,22 +247,17 @@
         private LocationRequest mProviderLocationRequest;
         private boolean mIsUsingHighPower;
 
-        @Nullable private Location mLastLocation = null;
+        private @Nullable Location mLastLocation = null;
 
         protected Registration(LocationRequest request, CallerIdentity identity,
                 LocationTransport transport, @PermissionLevel int permissionLevel) {
             super(Objects.requireNonNull(request), identity, transport);
 
             Preconditions.checkArgument(permissionLevel > PERMISSION_NONE);
+            Preconditions.checkArgument(!request.getWorkSource().isEmpty());
+
             mPermissionLevel = permissionLevel;
-
-            if (request.getWorkSource() != null && !request.getWorkSource().isEmpty()) {
-                mWorkSource = request.getWorkSource();
-            } else {
-                mWorkSource = identity.addToWorkSource(null);
-            }
-
-            mProviderLocationRequest = super.getRequest();
+            mProviderLocationRequest = request;
         }
 
         @GuardedBy("mLock")
@@ -376,6 +371,10 @@
             return mLastLocation;
         }
 
+        public @PermissionLevel int getPermissionLevel() {
+            return mPermissionLevel;
+        }
+
         public final boolean isForeground() {
             return mForeground;
         }
@@ -389,10 +388,6 @@
             return LocationProviderManager.this;
         }
 
-        protected final WorkSource getWorkSource() {
-            return mWorkSource;
-        }
-
         @GuardedBy("mLock")
         private void onHighPowerUsageChanged() {
             boolean isUsingHighPower = isUsingHighPower();
@@ -524,16 +519,18 @@
                 }
             }
 
-            if (baseRequest.isLocationSettingsIgnored()) {
+            boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored();
+            if (locationSettingsIgnored) {
                 // if we are not currently allowed use location settings ignored, disable it
                 if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
                         getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider(
                         null, getIdentity())) {
                     builder.setLocationSettingsIgnored(false);
+                    locationSettingsIgnored = false;
                 }
             }
 
-            if (!baseRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) {
+            if (!locationSettingsIgnored && !isThrottlingExempt()) {
                 // throttle in the background
                 if (!mForeground) {
                     builder.setIntervalMillis(max(baseRequest.getIntervalMillis(),
@@ -607,7 +604,7 @@
             mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
                     .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
             mWakeLock.setReferenceCounted(true);
-            mWakeLock.setWorkSource(getWorkSource());
+            mWakeLock.setWorkSource(request.getWorkSource());
         }
 
         @Override
@@ -626,7 +623,7 @@
                 onAlarm();
             } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
-                        getWorkSource());
+                        getRequest().getWorkSource());
             }
 
             // start listening for provider enabled/disabled events
@@ -688,7 +685,7 @@
                 if (maxLocationAgeMs > MIN_REQUEST_DELAY_MS) {
                     Location lastLocation = getLastLocationUnsafe(
                             getIdentity().getUserId(),
-                            PERMISSION_FINE, // acceptLocationChange() handles coarsening this
+                            getPermissionLevel(),
                             getRequest().isLocationSettingsIgnored(),
                             maxLocationAgeMs);
                     if (lastLocation != null) {
@@ -730,18 +727,8 @@
                 return null;
             }
 
-            Location location;
-            switch (mPermissionLevel) {
-                case PERMISSION_FINE:
-                    location = fineLocation;
-                    break;
-                case PERMISSION_COARSE:
-                    location = mLocationFudger.createCoarse(fineLocation);
-                    break;
-                default:
-                    // shouldn't be possible to have a client added without location permissions
-                    throw new AssertionError();
-            }
+            Location location = Objects.requireNonNull(
+                    getPermittedLocation(fineLocation, getPermissionLevel()));
 
             Location lastDeliveredLocation = getLastDeliveredLocation();
             if (lastDeliveredLocation != null) {
@@ -763,7 +750,7 @@
             }
 
             // note app ops
-            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
                     getIdentity())) {
                 if (D) {
                     Log.w(TAG, "noteOp denied for " + getIdentity());
@@ -960,7 +947,7 @@
     }
 
     protected final class GetCurrentLocationListenerRegistration extends Registration implements
-            IBinder.DeathRecipient, ProviderEnabledListener, OnAlarmListener {
+            IBinder.DeathRecipient, OnAlarmListener {
 
         private volatile LocationTransport mTransport;
 
@@ -972,11 +959,6 @@
             mTransport = transport;
         }
 
-        @GuardedBy("mLock")
-        void deliverLocation(@Nullable Location location) {
-            executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(location));
-        }
-
         @Override
         protected void onListenerUnregister() {
             mTransport = null;
@@ -999,29 +981,13 @@
                 onAlarm();
             } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
-                        getWorkSource());
-            }
-
-            // if this request is ignoring location settings, then we don't want to immediately fail
-            // it if the provider is disabled or becomes disabled.
-            if (!getRequest().isLocationSettingsIgnored()) {
-                // start listening for provider enabled/disabled events
-                addEnabledListener(this);
-
-                // if the provider is currently disabled fail immediately
-                int userId = getIdentity().getUserId();
-                if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) {
-                    deliverLocation(null);
-                }
+                        getRequest().getWorkSource());
             }
         }
 
         @GuardedBy("mLock")
         @Override
         protected void onProviderListenerUnregister() {
-            // stop listening for provider enabled/disabled events
-            removeEnabledListener(this);
-
             // remove alarm for expiration
             if (mExpirationRealtimeMs < Long.MAX_VALUE) {
                 mAlarmHelper.cancel(this);
@@ -1030,6 +996,38 @@
             ((IBinder) getKey()).unlinkToDeath(this, 0);
         }
 
+        @GuardedBy("mLock")
+        @Override
+        protected LocationListenerOperation onProviderListenerActive() {
+            Location lastLocation = getLastLocationUnsafe(
+                    getIdentity().getUserId(),
+                    getPermissionLevel(),
+                    getRequest().isLocationSettingsIgnored(),
+                    MAX_CURRENT_LOCATION_AGE_MS);
+            if (lastLocation != null) {
+                return acceptLocationChange(lastLocation);
+            }
+
+            return null;
+        }
+
+        @GuardedBy("mLock")
+        @Override
+        protected LocationListenerOperation onProviderListenerInactive() {
+            if (!getRequest().isLocationSettingsIgnored()) {
+                // if we go inactive for any reason, fail immediately
+                return acceptLocationChange(null);
+            }
+
+            return null;
+        }
+
+        void deliverNull() {
+            synchronized (mLock) {
+                executeSafely(getExecutor(), () -> mTransport, acceptLocationChange(null));
+            }
+        }
+
         @Override
         public void onAlarm() {
             if (D) {
@@ -1039,9 +1037,10 @@
             }
 
             synchronized (mLock) {
-                deliverLocation(null);
                 // no need to remove alarm after it's fired
                 mExpirationRealtimeMs = Long.MAX_VALUE;
+
+                deliverNull();
             }
         }
 
@@ -1060,29 +1059,16 @@
             }
 
             // lastly - note app ops
-            Location location;
-            if (fineLocation == null) {
-                location = null;
-            } else if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(mPermissionLevel),
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(getPermissionLevel()),
                     getIdentity())) {
                 if (D) {
                     Log.w(TAG, "noteOp denied for " + getIdentity());
                 }
-                location = null;
-            } else {
-                switch (mPermissionLevel) {
-                    case PERMISSION_FINE:
-                        location = fineLocation;
-                        break;
-                    case PERMISSION_COARSE:
-                        location = mLocationFudger.createCoarse(fineLocation);
-                        break;
-                    default:
-                        // shouldn't be possible to have a client added without location permissions
-                        throw new AssertionError();
-                }
+                fineLocation = null;
             }
 
+            Location location = getPermittedLocation(fineLocation, getPermissionLevel());
+
             return new LocationListenerOperation() {
                 @Override
                 public Location getLocation() {
@@ -1118,22 +1104,6 @@
         }
 
         @Override
-        public void onProviderEnabledChanged(String provider, int userId, boolean enabled) {
-            Preconditions.checkState(mName.equals(provider));
-
-            if (userId != getIdentity().getUserId()) {
-                return;
-            }
-
-            // if the provider is disabled we give up on current location immediately
-            if (!getRequest().isLocationSettingsIgnored() && !enabled) {
-                synchronized (mLock) {
-                    deliverLocation(null);
-                }
-            }
-        }
-
-        @Override
         public void binderDied() {
             try {
                 if (D) {
@@ -1172,7 +1142,7 @@
 
     protected final LocationManagerInternal mLocationManagerInternal;
     protected final SettingsHelper mSettingsHelper;
-    protected final UserInfoHelper mUserInfoHelper;
+    protected final UserInfoHelper mUserHelper;
     protected final AlarmHelper mAlarmHelper;
     protected final AppOpsHelper mAppOpsHelper;
     protected final LocationPermissionsHelper mLocationPermissionsHelper;
@@ -1234,7 +1204,7 @@
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
         mSettingsHelper = injector.getSettingsHelper();
-        mUserInfoHelper = injector.getUserInfoHelper();
+        mUserHelper = injector.getUserInfoHelper();
         mAlarmHelper = injector.getAlarmHelper();
         mAppOpsHelper = injector.getAppOpsHelper();
         mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
@@ -1259,7 +1229,7 @@
         synchronized (mLock) {
             mStarted = true;
 
-            mUserInfoHelper.addListener(mUserChangedListener);
+            mUserHelper.addListener(mUserChangedListener);
             mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
             long identity = Binder.clearCallingIdentity();
@@ -1274,20 +1244,19 @@
 
     public void stopManager() {
         synchronized (mLock) {
-            mUserInfoHelper.removeListener(mUserChangedListener);
+            mUserHelper.removeListener(mUserChangedListener);
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
-            // notify and remove all listeners
+            mStarted = false;
+
             long identity = Binder.clearCallingIdentity();
             try {
-                onUserStopped(UserHandle.USER_ALL);
+                onEnabledChanged(UserHandle.USER_ALL);
                 removeRegistrationIf(key -> true);
+                mEnabledListeners.clear();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-
-            mEnabledListeners.clear();
-            mStarted = false;
         }
     }
 
@@ -1439,31 +1408,43 @@
                 identity.getPackageName())) {
             return null;
         }
-        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
-            return null;
+        if (!ignoreLocationSettings) {
+            if (!isEnabled(identity.getUserId())) {
+                return null;
+            }
+            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
+                return null;
+            }
         }
-        if (!ignoreLocationSettings && !isEnabled(identity.getUserId())) {
+
+        // lastly - note app ops
+        if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                identity)) {
             return null;
         }
 
-        Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel,
-                ignoreLocationSettings, Long.MAX_VALUE);
+        Location location = getPermittedLocation(
+                getLastLocationUnsafe(
+                        identity.getUserId(),
+                        permissionLevel,
+                        ignoreLocationSettings,
+                        Long.MAX_VALUE),
+                permissionLevel);
 
-        // we don't note op here because we don't know what the client intends to do with the
-        // location, the client is responsible for noting if necessary
-
-        if (identity.getPid() == Process.myPid() && location != null) {
+        if (location != null && identity.getPid() == Process.myPid()) {
             // if delivering to the same process, make a copy of the location first (since
             // location is mutable)
-            return new Location(location);
-        } else {
-            return location;
+            location = new Location(location);
         }
+
+        return location;
     }
 
     /**
      * This function does not perform any permissions or safety checks, by calling it you are
-     * committing to performing all applicable checks yourself.
+     * committing to performing all applicable checks yourself. This always returns a "fine"
+     * location, even if the permissionLevel is coarse. You are responsible for coarsening the
+     * location if necessary.
      */
     @Nullable
     public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
@@ -1471,7 +1452,7 @@
         if (userId == UserHandle.USER_ALL) {
             // find the most recent location across all users
             Location lastLocation = null;
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
                         ignoreLocationSettings, maximumAgeMs);
@@ -1516,7 +1497,7 @@
 
     private void setLastLocation(Location location, int userId) {
         if (userId == UserHandle.USER_ALL) {
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 setLastLocation(location, runningUserIds[i]);
             }
@@ -1532,17 +1513,16 @@
                 mLastLocations.put(userId, lastLocation);
             }
 
-            Location coarseLocation = mLocationFudger.createCoarse(location);
             if (isEnabled(userId)) {
-                lastLocation.set(location, coarseLocation);
+                lastLocation.set(location);
             }
-            lastLocation.setBypass(location, coarseLocation);
+            lastLocation.setBypass(location);
         }
     }
 
     @Nullable
     public ICancellationSignal getCurrentLocation(LocationRequest request,
-            CallerIdentity callerIdentity, int permissionLevel, ILocationCallback callback) {
+            CallerIdentity identity, int permissionLevel, ILocationCallback callback) {
         if (request.getDurationMillis() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
             request = new LocationRequest.Builder(request)
                     .setDurationMillis(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS)
@@ -1552,52 +1532,31 @@
         GetCurrentLocationListenerRegistration registration =
                 new GetCurrentLocationListenerRegistration(
                         request,
-                        callerIdentity,
+                        identity,
                         new GetCurrentLocationTransport(callback),
                         permissionLevel);
 
         synchronized (mLock) {
-            if (mSettingsHelper.isLocationPackageBlacklisted(callerIdentity.getUserId(),
-                    callerIdentity.getPackageName())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-            if (!mUserInfoHelper.isCurrentUserId(callerIdentity.getUserId())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-            if (!request.isLocationSettingsIgnored() && !isEnabled(callerIdentity.getUserId())) {
-                registration.deliverLocation(null);
-                return null;
-            }
-
-            Location lastLocation = getLastLocationUnsafe(
-                    callerIdentity.getUserId(),
-                    permissionLevel,
-                    request.isLocationSettingsIgnored(),
-                    MAX_CURRENT_LOCATION_AGE_MS);
-            if (lastLocation != null) {
-                registration.deliverLocation(lastLocation);
-                return null;
-            }
-
-            // if last location isn't good enough then we add a location request
-            long identity = Binder.clearCallingIdentity();
+            long ident = Binder.clearCallingIdentity();
             try {
                 addRegistration(callback.asBinder(), registration);
+                if (!registration.isActive()) {
+                    // if the registration never activated, fail it immediately
+                    registration.deliverNull();
+                }
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                Binder.restoreCallingIdentity(ident);
             }
         }
 
         ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-        CancellationSignal cancellationSignal = CancellationSignal.fromTransport(cancelTransport);
-        cancellationSignal.setOnCancelListener(SingleUseCallback.wrap(
-                () -> {
-                    synchronized (mLock) {
-                        removeRegistration(callback.asBinder(), registration);
-                    }
-                }));
+        CancellationSignal.fromTransport(cancelTransport)
+                .setOnCancelListener(SingleUseCallback.wrap(
+                        () -> {
+                            synchronized (mLock) {
+                                removeRegistration(callback.asBinder(), registration);
+                            }
+                        }));
         return cancelTransport;
     }
 
@@ -1610,36 +1569,36 @@
         }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
             @PermissionLevel int permissionLevel, ILocationListener listener) {
+        LocationListenerRegistration registration = new LocationListenerRegistration(
+                request,
+                identity,
+                new LocationListenerTransport(listener),
+                permissionLevel);
+
         synchronized (mLock) {
-            long identity = Binder.clearCallingIdentity();
+            long ident = Binder.clearCallingIdentity();
             try {
-                addRegistration(
-                        listener.asBinder(),
-                        new LocationListenerRegistration(
-                                request,
-                                callerIdentity,
-                                new LocationListenerTransport(listener),
-                                permissionLevel));
+                addRegistration(listener.asBinder(), registration);
             } finally {
-                Binder.restoreCallingIdentity(identity);
+                Binder.restoreCallingIdentity(ident);
             }
         }
     }
 
     public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
+        LocationPendingIntentRegistration registration = new LocationPendingIntentRegistration(
+                request,
+                callerIdentity,
+                new LocationPendingIntentTransport(mContext, pendingIntent),
+                permissionLevel);
+
         synchronized (mLock) {
             long identity = Binder.clearCallingIdentity();
             try {
-                addRegistration(
-                        pendingIntent,
-                        new LocationPendingIntentRegistration(
-                                request,
-                                callerIdentity,
-                                new LocationPendingIntentTransport(mContext, pendingIntent),
-                                permissionLevel));
+                addRegistration(pendingIntent, registration);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1850,6 +1809,9 @@
             if (!isEnabled(identity.getUserId())) {
                 return false;
             }
+            if (!identity.isSystem() && !mUserHelper.isCurrentUserId(identity.getUserId())) {
+                return false;
+            }
 
             switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
                 case LOCATION_MODE_FOREGROUND_ONLY:
@@ -1887,7 +1849,7 @@
             Preconditions.checkState(Thread.holdsLock(mLock));
         }
 
-        long intervalMs = Long.MAX_VALUE;
+        long intervalMs = INTERVAL_DISABLED;
         boolean locationSettingsIgnored = false;
         boolean lowPower = true;
         ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size());
@@ -1906,12 +1868,18 @@
             locationRequests.add(request);
         }
 
+        if (intervalMs == INTERVAL_DISABLED) {
+            return EMPTY_REQUEST;
+        }
+
         // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold
         // interval slightly higher that the minimum interval, and spread the blame across all
         // contributing registrations under that threshold (since worksource does not allow us to
         // represent differing power blame ratios).
-        long thresholdIntervalMs = (intervalMs + 1000) * 3 / 2;
-        if (thresholdIntervalMs < 0 || thresholdIntervalMs >= PASSIVE_INTERVAL) {
+        long thresholdIntervalMs;
+        try {
+            thresholdIntervalMs = Math.multiplyExact(Math.addExact(intervalMs, 1000) / 2, 3);
+        } catch (ArithmeticException e) {
             // check for and handle overflow by setting to one below the passive interval so passive
             // requests are automatically skipped
             thresholdIntervalMs = PASSIVE_INTERVAL - 1;
@@ -1920,7 +1888,7 @@
         WorkSource workSource = new WorkSource();
         for (Registration registration : registrations) {
             if (registration.getRequest().getIntervalMillis() <= thresholdIntervalMs) {
-                workSource.add(registration.getWorkSource());
+                workSource.add(registration.getRequest().getWorkSource());
             }
         }
 
@@ -1953,7 +1921,7 @@
                 // location for our calculations instead. this prevents spammy add/remove behavior
                 last = getLastLocationUnsafe(
                         registration.getIdentity().getUserId(),
-                        PERMISSION_FINE,
+                        registration.getPermissionLevel(),
                         false,
                         locationRequest.getIntervalMillis());
             }
@@ -1978,7 +1946,8 @@
         synchronized (mLock) {
             switch (change) {
                 case UserListener.CURRENT_USER_CHANGED:
-                    onEnabledChanged(userId);
+                    updateRegistrations(
+                            registration -> registration.getIdentity().getUserId() == userId);
                     break;
                 case UserListener.USER_STARTED:
                     onUserStarted(userId);
@@ -2159,13 +2128,10 @@
         }
 
         if (userId == UserHandle.USER_ALL) {
-            onEnabledChanged(UserHandle.USER_ALL);
             mEnabled.clear();
             mLastLocations.clear();
         } else {
             Preconditions.checkArgument(userId >= 0);
-
-            onEnabledChanged(userId);
             mEnabled.delete(userId);
             mLastLocations.remove(userId);
         }
@@ -2182,7 +2148,7 @@
             // settings for instance) do not support the null user
             return;
         } else if (userId == UserHandle.USER_ALL) {
-            final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
+            final int[] runningUserIds = mUserHelper.getRunningUserIds();
             for (int i = 0; i < runningUserIds.length; i++) {
                 onEnabledChanged(runningUserIds[i]);
             }
@@ -2193,7 +2159,6 @@
 
         boolean enabled = mStarted
                 && mProvider.getState().allowed
-                && mUserInfoHelper.isCurrentUserId(userId)
                 && mSettingsHelper.isLocationEnabled(userId);
 
         int index = mEnabled.indexOfKey(userId);
@@ -2249,6 +2214,20 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
+    @Nullable
+    private Location getPermittedLocation(@Nullable Location fineLocation,
+            @PermissionLevel int permissionLevel) {
+        switch (permissionLevel) {
+            case PERMISSION_FINE:
+                return fineLocation;
+            case PERMISSION_COARSE:
+                return fineLocation != null ? mLocationFudger.createCoarse(fineLocation) : null;
+            default:
+                // shouldn't be possible to have a client added without location permissions
+                throw new AssertionError();
+        }
+    }
+
     public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
         synchronized (mLock) {
             ipw.print(mName);
@@ -2261,7 +2240,7 @@
 
             super.dump(fd, ipw, args);
 
-            int[] userIds = mUserInfoHelper.getRunningUserIds();
+            int[] userIds = mUserHelper.getRunningUserIds();
             for (int userId : userIds) {
                 if (userIds.length != 1) {
                     ipw.print("user ");
@@ -2299,10 +2278,14 @@
         public void clearMock() {
             if (mFineLocation != null && mFineLocation.isFromMockProvider()) {
                 mFineLocation = null;
+            }
+            if (mCoarseLocation != null && mCoarseLocation.isFromMockProvider()) {
                 mCoarseLocation = null;
             }
             if (mFineBypassLocation != null && mFineBypassLocation.isFromMockProvider()) {
                 mFineBypassLocation = null;
+            }
+            if (mCoarseBypassLocation != null && mCoarseBypassLocation.isFromMockProvider()) {
                 mCoarseBypassLocation = null;
             }
         }
@@ -2333,14 +2316,14 @@
             }
         }
 
-        public void set(Location fineLocation, Location coarseLocation) {
-            mFineLocation = calculateNextFine(mFineLocation, fineLocation);
-            mCoarseLocation = calculateNextCoarse(mCoarseLocation, coarseLocation);
+        public void set(Location location) {
+            mFineLocation = calculateNextFine(mFineLocation, location);
+            mCoarseLocation = calculateNextCoarse(mCoarseLocation, location);
         }
 
-        public void setBypass(Location fineLocation, Location coarseLocation) {
-            mFineBypassLocation = calculateNextFine(mFineBypassLocation, fineLocation);
-            mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, coarseLocation);
+        public void setBypass(Location location) {
+            mFineBypassLocation = calculateNextFine(mFineBypassLocation, location);
+            mCoarseBypassLocation = calculateNextCoarse(mCoarseBypassLocation, location);
         }
 
         private Location calculateNextFine(@Nullable Location oldFine, Location newFine) {
@@ -2362,8 +2345,8 @@
             }
 
             // update last coarse interval only if enough time has passed
-            if (newCoarse.getElapsedRealtimeNanos() - MIN_COARSE_INTERVAL_MS
-                    > oldCoarse.getElapsedRealtimeNanos()) {
+            if (newCoarse.getElapsedRealtimeMillis() - MIN_COARSE_INTERVAL_MS
+                    > oldCoarse.getElapsedRealtimeMillis()) {
                 return newCoarse;
             } else {
                 return oldCoarse;
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 58e6d59..baa01b1 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -327,7 +327,7 @@
     protected boolean isActive(GeofenceRegistration registration) {
         CallerIdentity identity = registration.getIdentity();
         return registration.isPermitted()
-                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
+                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
                 identity.getPackageName());
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index e782d5e..1cdb118 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -259,7 +259,7 @@
         CallerIdentity identity = registration.getIdentity();
         return registration.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
-                && mUserInfoHelper.isCurrentUserId(identity.getUserId())
+                && (identity.isSystem() || mUserInfoHelper.isCurrentUserId(identity.getUserId()))
                 && mLocationManagerInternal.isProviderEnabledForUser(GPS_PROVIDER,
                 identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index 99414c4..d482637 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -44,16 +44,22 @@
 import java.util.Objects;
 
 /**
- * A real implementation of {@link LocationTimeZoneProviderController} that supports a single
- * {@link LocationTimeZoneProvider}.
+ * A real implementation of {@link LocationTimeZoneProviderController} that supports a primary and a
+ * secondary {@link LocationTimeZoneProvider}.
  *
- * TODO(b/152744911): This implementation currently only supports a single ("primary") provider.
- *  Support for a secondary provider will be added in a later commit.
+ * <p>The primary is used until it fails or becomes uncertain. The secondary will then be enabled.
+ * The controller will immediately make suggestions based on "certain" {@link
+ * LocationTimeZoneEvent}s, i.e. events that demonstrate the provider is certain what the time zone
+ * is. The controller will not make immediate suggestions based on "uncertain" events, giving
+ * providers time to change their mind. This also gives the secondary provider time to initialize
+ * when the primary becomes uncertain.
  */
 class ControllerImpl extends LocationTimeZoneProviderController {
 
     @NonNull private final LocationTimeZoneProvider mPrimaryProvider;
 
+    @NonNull private final LocationTimeZoneProvider mSecondaryProvider;
+
     @GuardedBy("mSharedLock")
     // Non-null after initialize()
     private ConfigurationInternal mCurrentUserConfiguration;
@@ -67,7 +73,9 @@
     private Callback mCallback;
 
     /**
-     * Used for scheduling uncertainty timeouts, i.e after the provider has reported uncertainty.
+     * Used for scheduling uncertainty timeouts, i.e after a provider has reported uncertainty.
+     * This timeout is not provider-specific: it is started when the controller becomes uncertain
+     * due to events it has received from one or other provider.
      */
     @NonNull private final SingleRunnableQueue mUncertaintyTimeoutQueue;
 
@@ -77,10 +85,12 @@
     private GeolocationTimeZoneSuggestion mLastSuggestion;
 
     ControllerImpl(@NonNull ThreadingDomain threadingDomain,
-            @NonNull LocationTimeZoneProvider primaryProvider) {
+            @NonNull LocationTimeZoneProvider primaryProvider,
+            @NonNull LocationTimeZoneProvider secondaryProvider) {
         super(threadingDomain);
         mUncertaintyTimeoutQueue = threadingDomain.createSingleRunnableQueue();
         mPrimaryProvider = Objects.requireNonNull(primaryProvider);
+        mSecondaryProvider = Objects.requireNonNull(secondaryProvider);
     }
 
     @Override
@@ -96,8 +106,9 @@
             LocationTimeZoneProvider.ProviderListener providerListener =
                     ControllerImpl.this::onProviderStateChange;
             mPrimaryProvider.initialize(providerListener);
+            mSecondaryProvider.initialize(providerListener);
 
-            alterProviderEnabledStateIfRequired(
+            alterProvidersEnabledStateIfRequired(
                     null /* oldConfiguration */, mCurrentUserConfiguration);
         }
     }
@@ -115,15 +126,15 @@
 
             if (!newConfig.equals(oldConfig)) {
                 if (newConfig.getUserId() != oldConfig.getUserId()) {
-                    // If the user changed, disable the provider if needed. It may be re-enabled for
-                    // the new user below if their settings allow.
+                    // If the user changed, disable the providers if needed. They may be re-enabled
+                    // for the new user immediately afterwards if their settings allow.
                     debugLog("User changed. old=" + oldConfig.getUserId()
-                            + ", new=" + newConfig.getUserId() + ": Disabling provider");
-                    disableProvider();
+                            + ", new=" + newConfig.getUserId() + ": Disabling providers");
+                    disableProviders();
 
-                    alterProviderEnabledStateIfRequired(null /* oldConfiguration */, newConfig);
+                    alterProvidersEnabledStateIfRequired(null /* oldConfiguration */, newConfig);
                 } else {
-                    alterProviderEnabledStateIfRequired(oldConfig, newConfig);
+                    alterProvidersEnabledStateIfRequired(oldConfig, newConfig);
                 }
             }
         }
@@ -140,10 +151,11 @@
     }
 
     @GuardedBy("mSharedLock")
-    private void disableProvider() {
+    private void disableProviders() {
         disableProviderIfEnabled(mPrimaryProvider);
+        disableProviderIfEnabled(mSecondaryProvider);
 
-        // By definition, if the provider is disabled, the controller is uncertain.
+        // By definition, if both providers are disabled, the controller is uncertain.
         cancelUncertaintyTimeout();
     }
 
@@ -181,7 +193,7 @@
     }
 
     /**
-     * Sets the provider into the correct enabled/disabled state for the {@code newConfiguration}
+     * Sets the providers into the correct enabled/disabled state for the {@code newConfiguration}
      * and, if there is a provider state change, makes any suggestions required to inform the
      * downstream time zone detection code.
      *
@@ -190,7 +202,7 @@
      * or when a new configuration has been received.
      */
     @GuardedBy("mSharedLock")
-    private void alterProviderEnabledStateIfRequired(
+    private void alterProvidersEnabledStateIfRequired(
             @Nullable ConfigurationInternal oldConfiguration,
             @NonNull ConfigurationInternal newConfiguration) {
 
@@ -203,21 +215,40 @@
             return;
         }
 
+        // The check above ensures that the logic below only executes if providers are going from
+        // {enabled *} -> {disabled}, or {disabled} -> {enabled initializing}. If this changes in
+        // future and there could be {enabled *} -> {enabled *} cases, or cases where the provider
+        // can't be assumed to go straight to the {enabled initializing} state, then the logic below
+        // would need to cover extra conditions, for example:
+        // 1) If the primary is in {enabled uncertain}, the secondary should be enabled.
+        // 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty
+        //    timeout started when the primary entered {enabled uncertain} should be cancelled.
+
         if (newGeoDetectionEnabled) {
             // Try to enable the primary provider.
             tryEnableProvider(mPrimaryProvider, newConfiguration);
 
+            // The secondary should only ever be enabled if the primary now isn't enabled (i.e. it
+            // couldn't become {enabled initializing} because it is {perm failed}).
             ProviderState newPrimaryState = mPrimaryProvider.getCurrentState();
             if (!newPrimaryState.isEnabled()) {
-                // If the provider is perm failed then the controller is immediately considered
-                // uncertain.
-                GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
-                        "Provider is failed:"
-                                + " primary=" + mPrimaryProvider.getCurrentState());
-                makeSuggestion(suggestion);
+                // If the primary provider is {perm failed} then the controller must try to enable
+                // the secondary.
+                tryEnableProvider(mSecondaryProvider, newConfiguration);
+
+                ProviderState newSecondaryState = mSecondaryProvider.getCurrentState();
+                if (!newSecondaryState.isEnabled()) {
+                    // If both providers are {perm failed} then the controller immediately
+                    // becomes uncertain.
+                    GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                            "Providers are failed:"
+                                    + " primary=" + mPrimaryProvider.getCurrentState()
+                                    + " secondary=" + mPrimaryProvider.getCurrentState());
+                    makeSuggestion(suggestion);
+                }
             }
         } else {
-            disableProvider();
+            disableProviders();
 
             // There can be an uncertainty timeout set if the controller most recently received
             // an uncertain event. This is a no-op if there isn't a timeout set.
@@ -300,35 +331,63 @@
     }
 
     private void assertProviderKnown(@NonNull LocationTimeZoneProvider provider) {
-        if (provider != mPrimaryProvider) {
+        if (provider != mPrimaryProvider && provider != mSecondaryProvider) {
             throw new IllegalArgumentException("Unknown provider: " + provider);
         }
     }
 
     /**
-     * Called when the provider has reported that it has failed permanently.
+     * Called when a provider has reported that it has failed permanently.
      */
     @GuardedBy("mSharedLock")
     private void handleProviderFailedStateChange(@NonNull ProviderState providerState) {
         LocationTimeZoneProvider failedProvider = providerState.provider;
+        ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState();
+        ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState();
 
-        // If the provider is newly perm failed then the controller is uncertain by
-        // definition.
-        cancelUncertaintyTimeout();
+        // If a provider has failed, the other may need to be enabled.
+        if (failedProvider == mPrimaryProvider) {
+            if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
+                // The primary must have failed. Try to enable the secondary. This does nothing if
+                // the provider is already enabled, and will leave the provider in
+                // {enabled initializing} if the provider is disabled.
+                tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration);
+            }
+        } else if (failedProvider == mSecondaryProvider) {
+            // No-op: The secondary will only be active if the primary is uncertain or is failed.
+            // So, there the primary should not need to be enabled when the secondary fails.
+            if (primaryCurrentState.stateEnum != PROVIDER_STATE_ENABLED_UNCERTAIN
+                    && primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
+                warnLog("Secondary provider unexpected reported a failure:"
+                        + " failed provider=" + failedProvider.getName()
+                        + ", primary provider=" + mPrimaryProvider
+                        + ", secondary provider=" + mSecondaryProvider);
+            }
+        }
 
-        // If the provider is now failed, then we must send a suggestion informing the time
-        // zone detector that there are no further updates coming in future.
+        // If both providers are now failed, the controller needs to tell the next component in the
+        // time zone detection process.
+        if (primaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED
+                && secondaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED) {
 
-        GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
-                "The provider is permanently failed:"
-                        + " provider=" + failedProvider);
-        makeSuggestion(suggestion);
+            // If both providers are newly failed then the controller is uncertain by definition
+            // and it will never recover so it can send a suggestion immediately.
+            cancelUncertaintyTimeout();
+
+            // If both providers are now failed, then a suggestion must be sent informing the time
+            // zone detector that there are no further updates coming in future.
+            GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
+                    "Both providers are permanently failed:"
+                            + " primary=" + primaryCurrentState.provider
+                            + ", secondary=" + secondaryCurrentState.provider);
+            makeSuggestion(suggestion);
+        }
     }
 
     /**
      * Called when a provider has changed state but just moved from one enabled state to another
      * enabled state, usually as a result of a new {@link LocationTimeZoneEvent} being received.
-     * However, there are rare cases where the event can be null.
+     * However, there are rare cases where the event can also be null.
      */
     @GuardedBy("mSharedLock")
     private void handleProviderEnabledStateChange(@NonNull ProviderState providerState) {
@@ -395,6 +454,10 @@
         // By definition, the controller is now certain.
         cancelUncertaintyTimeout();
 
+        if (provider == mPrimaryProvider) {
+            disableProviderIfEnabled(mSecondaryProvider);
+        }
+
         GeolocationTimeZoneSuggestion suggestion =
                 new GeolocationTimeZoneSuggestion(timeZoneIds);
         suggestion.addDebugInfo(reason);
@@ -421,6 +484,11 @@
             mPrimaryProvider.dump(ipw, args);
             ipw.decreaseIndent(); // level 2
 
+            ipw.println("Secondary Provider:");
+            ipw.increaseIndent(); // level 2
+            mSecondaryProvider.dump(ipw, args);
+            ipw.decreaseIndent(); // level 2
+
             ipw.decreaseIndent(); // level 1
         }
     }
@@ -470,6 +538,14 @@
             mUncertaintyTimeoutQueue.runDelayed(() -> onProviderUncertaintyTimeout(provider),
                     delay.toMillis());
         }
+
+        if (provider == mPrimaryProvider) {
+            // (Try to) enable the secondary. It could already be enabled, or enabling might not
+            // succeed if the provider has previously reported it is perm failed. The uncertainty
+            // timeout (set above) is used to ensure that an uncertain suggestion will be made if
+            // the secondary cannot generate a success event in time.
+            tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration);
+        }
     }
 
     private void onProviderUncertaintyTimeout(@NonNull LocationTimeZoneProvider provider) {
@@ -478,7 +554,8 @@
         synchronized (mSharedLock) {
             GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
                     "Uncertainty timeout triggered for " + provider.getName() + ":"
-                            + " primary=" + mPrimaryProvider);
+                            + " primary=" + mPrimaryProvider
+                            + ", secondary=" + mSecondaryProvider);
             makeSuggestion(suggestion);
         }
     }
@@ -498,6 +575,8 @@
         LocationTimeZoneProvider targetProvider;
         if (Objects.equals(mPrimaryProvider.getName(), targetProviderName)) {
             targetProvider = mPrimaryProvider;
+        } else if (Objects.equals(mSecondaryProvider.getName(), targetProviderName)) {
+            targetProvider = mSecondaryProvider;
         } else {
             warnLog("Unable to process simulated binder provider event,"
                     + " unknown providerName in event=" + event);
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index 97d3b82..a817759 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -44,8 +44,8 @@
  * are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that
  * offer {@link android.location.timezone.LocationTimeZoneEvent}s.
  *
- * TODO(b/152744911): This implementation currently only supports a primary provider. Support for a
- *  secondary provider must be added in a later commit.
+ * <p>For details of the time zone suggestion behavior, see {@link
+ * LocationTimeZoneProviderController}.
  *
  * <p>Implementation details:
  *
@@ -109,6 +109,7 @@
     static final String TAG = "LocationTZDetector";
 
     static final String PRIMARY_PROVIDER_NAME = "primary";
+    static final String SECONDARY_PROVIDER_NAME = "secondary";
 
     private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX =
             "persist.sys.location_tz_simulation_mode.";
@@ -117,6 +118,8 @@
 
     private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
             "com.android.location.timezone.service.v1.PrimaryLocationTimeZoneProvider";
+    private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
+            "com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider";
 
 
     @NonNull private final Context mContext;
@@ -160,8 +163,9 @@
         // Called on an arbitrary thread during initialization.
         synchronized (mSharedLock) {
             LocationTimeZoneProvider primary = createPrimaryProvider();
+            LocationTimeZoneProvider secondary = createSecondaryProvider();
             mLocationTimeZoneDetectorController =
-                    new ControllerImpl(mThreadingDomain, primary);
+                    new ControllerImpl(mThreadingDomain, primary, secondary);
             ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
             ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
                     mThreadingDomain, mLocationTimeZoneDetectorController);
@@ -189,6 +193,23 @@
         return createLocationTimeZoneProvider(PRIMARY_PROVIDER_NAME, proxy);
     }
 
+    private LocationTimeZoneProvider createSecondaryProvider() {
+        LocationTimeZoneProviderProxy proxy;
+        if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) {
+            proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+        } else {
+            proxy = RealLocationTimeZoneProviderProxy.createAndRegister(
+                    mContext,
+                    mThreadingDomain,
+                    SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
+                    com.android.internal.R.bool.config_enableSecondaryLocationTimeZoneOverlay,
+                    com.android.internal.R.string
+                            .config_secondaryLocationTimeZoneProviderPackageName
+            );
+        }
+        return createLocationTimeZoneProvider(SECONDARY_PROVIDER_NAME, proxy);
+    }
+
     private boolean isInSimulationMode(String providerName) {
         return SystemProperties.getBoolean(
                 SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false);
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
index ef2e349..f1d3723 100644
--- a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
+++ b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
@@ -21,6 +21,7 @@
 import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
 
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME;
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.SECONDARY_PROVIDER_NAME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,7 +43,8 @@
  */
 final class SimulatedBinderProviderEvent {
 
-    private static final List<String> VALID_PROVIDER_NAMES = Arrays.asList(PRIMARY_PROVIDER_NAME);
+    private static final List<String> VALID_PROVIDER_NAMES =
+            Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
 
     static final int INJECTED_EVENT_TYPE_ON_BIND = 1;
     static final int INJECTED_EVENT_TYPE_ON_UNBIND = 2;
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
index d85ca5e..6e4cf06 100644
--- a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -16,7 +16,13 @@
 
 package com.android.server.location.util;
 
+import static android.os.PowerManager.locationPowerSaveModeToString;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
 import android.os.PowerManager.LocationPowerSaveMode;
+import android.util.Log;
 
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -60,7 +66,12 @@
 
     protected final void notifyLocationPowerSaveModeChanged(
             @LocationPowerSaveMode int locationPowerSaveMode) {
+        if (D) {
+            Log.d(TAG, "location power save mode is now " + locationPowerSaveModeToString(
+                    locationPowerSaveMode));
+        }
         mLocationEventLog.logLocationPowerSaveMode(locationPowerSaveMode);
+
         for (LocationPowerSaveModeChangedListener listener : mListeners) {
             listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
         }
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
index d47bce3..ecd6966 100644
--- a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -16,6 +16,11 @@
 
 package com.android.server.location.util;
 
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
+import android.util.Log;
+
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -55,6 +60,10 @@
     }
 
     protected final void notifyScreenInteractiveChanged(boolean interactive) {
+        if (D) {
+            Log.d(TAG, "screen interactive is now " + interactive);
+        }
+
         for (ScreenInteractiveChangedListener listener : mListeners) {
             listener.onScreenInteractiveChanged(interactive);
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java
index 7c1c1c7..5a8fbf3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsAccess.java
+++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java
@@ -24,7 +24,6 @@
 import android.Manifest;
 import android.annotation.IntDef;
 import android.app.AppOpsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -111,8 +110,7 @@
         boolean hasCarrierPrivileges = tm != null &&
                 tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) ==
                         TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
-        boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
-                DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid);
         if (hasCarrierPrivileges || isDeviceOwner
                 || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) {
             // Carrier-privileged apps and device owners, and the system can access data usage for
@@ -126,8 +124,9 @@
             return NetworkStatsAccess.Level.DEVICESUMMARY;
         }
 
-        boolean isProfileOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid,
-                DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
+        boolean isProfileOwner = dpmi != null && (dpmi.isActiveProfileOwner(callingUid)
+                || dpmi.isActiveDeviceOwner(callingUid));
         if (isProfileOwner) {
             // Apps with the AppOps permission, profile owners, and apps with the privileged
             // permission can access data usage for all apps in this user/profile.
diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java
index a4a94c2..25ad9280 100644
--- a/services/core/java/com/android/server/notification/EventConditionProvider.java
+++ b/services/core/java/com/android/server/notification/EventConditionProvider.java
@@ -271,7 +271,7 @@
                 new Intent(ACTION_EVALUATE)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                         .putExtra(EXTRA_TIME, time),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         alarms.cancel(pendingIntent);
         if (time == 0 || time < now) {
             if (DEBUG) Slog.d(TAG, "Not scheduling evaluate: " + (time == 0 ? "no time specified"
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b4c98e0..cab3377 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -137,7 +137,6 @@
 import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.backup.BackupManager;
 import android.app.role.OnRoleHoldersChangedListener;
@@ -3084,6 +3083,8 @@
                         UserHandle.getUserId(uid), REASON_PACKAGE_BANNED, null);
             }
 
+            mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
+                    enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
             try {
                 getContext().sendBroadcastAsUser(
                         new Intent(ACTION_APP_BLOCK_STATE_CHANGED)
@@ -4541,11 +4542,11 @@
             } catch (NameNotFoundException e) {
                 return false;
             }
+            //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
             return checkPackagePolicyAccess(pkg)
                     || mListeners.isComponentEnabledForPackage(pkg)
-                    || (mDpm != null &&
-                            mDpm.isActiveAdminWithPolicy(Binder.getCallingUid(),
-                                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER));
+                    || (mDpm != null && (mDpm.isActiveProfileOwner(Binder.getCallingUid())
+                                || mDpm.isActiveDeviceOwner(Binder.getCallingUid())));
         }
 
         @Override
@@ -5247,7 +5248,8 @@
                 Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg);
                 if (appIntent != null) {
                     summaryNotification.contentIntent = PendingIntent.getActivityAsUser(
-                            getContext(), 0, appIntent, 0, null, UserHandle.of(userId));
+                            getContext(), 0, appIntent, PendingIntent.FLAG_IMMUTABLE, null,
+                            UserHandle.of(userId));
                 }
                 final StatusBarNotification summarySbn =
                         new StatusBarNotification(adjustedSbn.getPackageName(),
@@ -6830,7 +6832,7 @@
                                     .appendPath(record.getKey()).build())
                             .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                             .putExtra(EXTRA_KEY, record.getKey()),
-                    PendingIntent.FLAG_UPDATE_CURRENT);
+                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                     SystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(), pi);
         }
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index 961d3db..3517033 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -223,7 +223,7 @@
                 new Intent(ACTION_EVALUATE)
                         .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                         .putExtra(EXTRA_TIME, time),
-                PendingIntent.FLAG_UPDATE_CURRENT);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         alarms.cancel(pendingIntent);
         if (time > now) {
             if (DEBUG) Slog.d(TAG, String.format("Scheduling evaluate for %s, in %s, now=%s",
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 8640dbc..7e8ff94 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -16,13 +16,13 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA256;
-import static android.content.pm.Checksum.PARTIAL_MERKLE_ROOT_1M_SHA512;
-import static android.content.pm.Checksum.WHOLE_MD5;
-import static android.content.pm.Checksum.WHOLE_MERKLE_ROOT_4K_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA1;
-import static android.content.pm.Checksum.WHOLE_SHA256;
-import static android.content.pm.Checksum.WHOLE_SHA512;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.Checksum.TYPE_WHOLE_MD5;
+import static android.content.pm.Checksum.TYPE_WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
+import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
 import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
@@ -182,13 +182,13 @@
                     dos.writeUTF(splitName);
                 }
 
-                dos.writeInt(checksum.getKind());
+                dos.writeInt(checksum.getType());
 
                 final byte[] valueBytes = checksum.getValue();
                 dos.writeInt(valueBytes.length);
                 dos.write(valueBytes);
 
-                final String packageName = checksum.getSourcePackageName();
+                final String packageName = checksum.getInstallerPackageName();
                 if (packageName == null) {
                     dos.writeInt(-1);
                 } else {
@@ -196,7 +196,7 @@
                     dos.writeUTF(packageName);
                 }
 
-                final Certificate cert = checksum.getSourceCertificate();
+                final Certificate cert = checksum.getInstallerCertificate();
                 final byte[] certBytes = (cert == null) ? null : cert.getEncoded();
                 if (certBytes == null) {
                     dos.writeInt(-1);
@@ -225,7 +225,7 @@
                     splitName = dis.readUTF();
                 }
 
-                final int kind = dis.readInt();
+                final int type = dis.readInt();
 
                 final byte[] valueBytes = new byte[dis.readInt()];
                 dis.read(valueBytes);
@@ -245,7 +245,7 @@
                     certBytes = new byte[certBytesLength];
                     dis.read(certBytes);
                 }
-                checksums[i] = new ApkChecksum(splitName, new Checksum(kind, valueBytes),
+                checksums[i] = new ApkChecksum(splitName, new Checksum(type, valueBytes),
                         packageName, certBytes);
             }
             return checksums;
@@ -265,8 +265,8 @@
      * @param statusReceiver    to receive the resulting checksums
      */
     public static void getChecksums(List<Pair<String, File>> filesToChecksum,
-            @Checksum.Kind int optional,
-            @Checksum.Kind int required,
+            @Checksum.Type int optional,
+            @Checksum.Type int required,
             @Nullable Certificate[] trustedInstallers,
             @NonNull IntentSender statusReceiver,
             @NonNull Injector injector) {
@@ -292,7 +292,7 @@
 
     private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
             List<Map<Integer, ApkChecksum>> result,
-            @Checksum.Kind int required,
+            @Checksum.Type int required,
             @NonNull IntentSender statusReceiver,
             @NonNull Injector injector,
             long startTime) {
@@ -339,32 +339,32 @@
      *
      * @param split             split name, null for base
      * @param file              to fetch checksums for
-     * @param kinds             mask to fetch checksums
+     * @param types             mask to fetch checksums
      * @param trustedInstallers array of certificate to trust, two specific cases:
      *                          null - trust anybody,
      *                          [] - trust nobody.
      * @param checksums         resulting checksums
      */
     private static void getAvailableApkChecksums(String split, File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             @Nullable Certificate[] trustedInstallers,
             Map<Integer, ApkChecksum> checksums) {
         final String filePath = file.getAbsolutePath();
 
         // Always available: FSI or IncFs.
-        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+        if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) {
             // Hashes in fs-verity and IncFS are always verified.
             ApkChecksum checksum = extractHashFromFS(split, filePath);
             if (checksum != null) {
-                checksums.put(checksum.getKind(), checksum);
+                checksums.put(checksum.getType(), checksum);
             }
         }
 
         // System enforced: v2/v3.
-        if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired(
-                PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+        if (isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums) || isRequired(
+                TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) {
             Map<Integer, ApkChecksum> v2v3checksums = extractHashFromV2V3Signature(
-                    split, filePath, kinds);
+                    split, filePath, types);
             if (v2v3checksums != null) {
                 checksums.putAll(v2v3checksums);
             }
@@ -377,9 +377,9 @@
                     final ApkChecksum[] digests = readChecksums(digestsFile);
                     final Set<Signature> trusted = convertToSet(trustedInstallers);
                     for (ApkChecksum digest : digests) {
-                        if (isRequired(digest.getKind(), kinds, checksums) && isTrusted(digest,
+                        if (isRequired(digest.getType(), types, checksums) && isTrusted(digest,
                                 trusted)) {
-                            checksums.put(digest.getKind(), digest);
+                            checksums.put(digest.getType(), digest);
                         }
                     }
                 } catch (IOException e) {
@@ -395,16 +395,16 @@
      * Whether the file is available for checksumming or we need to wait.
      */
     private static boolean needToWait(File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             Map<Integer, ApkChecksum> checksums,
             @NonNull Injector injector) throws IOException {
-        if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)
-                && !isRequired(WHOLE_MD5, kinds, checksums)
-                && !isRequired(WHOLE_SHA1, kinds, checksums)
-                && !isRequired(WHOLE_SHA256, kinds, checksums)
-                && !isRequired(WHOLE_SHA512, kinds, checksums)
-                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums)
-                && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) {
+        if (!isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)
+                && !isRequired(TYPE_WHOLE_MD5, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA1, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA256, types, checksums)
+                && !isRequired(TYPE_WHOLE_SHA512, types, checksums)
+                && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, types, checksums)
+                && !isRequired(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, types, checksums)) {
             return false;
         }
 
@@ -432,16 +432,16 @@
      *
      * @param split     split name, null for base
      * @param file      to fetch checksums for
-     * @param kinds     mask to forcefully calculate if not available
+     * @param types     mask to forcefully calculate if not available
      * @param checksums resulting checksums
      */
     private static void getRequiredApkChecksums(String split, File file,
-            @Checksum.Kind int kinds,
+            @Checksum.Type int types,
             Map<Integer, ApkChecksum> checksums) {
         final String filePath = file.getAbsolutePath();
 
         // Manually calculating required checksums if not readily available.
-        if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) {
+        if (isRequired(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, types, checksums)) {
             try {
                 byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash(
                         filePath, /*salt=*/null,
@@ -451,27 +451,28 @@
                                 return ByteBuffer.allocate(capacity);
                             }
                         });
-                checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
-                        new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+                checksums.put(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+                        new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+                                generatedRootHash));
             } catch (IOException | NoSuchAlgorithmException | DigestException e) {
                 Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
             }
         }
 
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256);
-        calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_MD5);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA1);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA256);
+        calculateChecksumIfRequested(checksums, split, file, types, TYPE_WHOLE_SHA512);
 
-        calculatePartialChecksumsIfRequested(checksums, split, file, kinds);
+        calculatePartialChecksumsIfRequested(checksums, split, file, types);
     }
 
-    private static boolean isRequired(@Checksum.Kind int kind,
-            @Checksum.Kind int kinds, Map<Integer, ApkChecksum> checksums) {
-        if ((kinds & kind) == 0) {
+    private static boolean isRequired(@Checksum.Type int type,
+            @Checksum.Type int types, Map<Integer, ApkChecksum> checksums) {
+        if ((types & type) == 0) {
             return false;
         }
-        if (checksums.containsKey(kind)) {
+        if (checksums.containsKey(type)) {
             return false;
         }
         return true;
@@ -497,7 +498,7 @@
         if (trusted == null) {
             return true;
         }
-        final Signature signature = new Signature(checksum.getSourceCertificateBytes());
+        final Signature signature = new Signature(checksum.getInstallerCertificateBytes());
         return trusted.contains(signature);
     }
 
@@ -506,7 +507,7 @@
         {
             byte[] hash = VerityUtils.getFsverityRootHash(filePath);
             if (hash != null) {
-                return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+                return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
             }
         }
         // v4 next
@@ -516,7 +517,7 @@
             byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
                     null);
             if (hash != null) {
-                return new ApkChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+                return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
             }
         } catch (SignatureNotFoundException e) {
             // Nothing
@@ -527,7 +528,7 @@
     }
 
     private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
-            String split, String filePath, int kinds) {
+            String split, String filePath, int types) {
         Map<Integer, byte[]> contentDigests = null;
         try {
             contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
@@ -544,56 +545,56 @@
         }
 
         Map<Integer, ApkChecksum> checksums = new ArrayMap<>();
-        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
+        if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
             byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
             if (hash != null) {
-                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
-                        new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+                checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+                        new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
             }
         }
-        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
+        if ((types & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
             byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
             if (hash != null) {
-                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
-                        new ApkChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+                checksums.put(TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
+                        new ApkChecksum(split, TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
             }
         }
         return checksums;
     }
 
-    private static String getMessageDigestAlgoForChecksumKind(int kind)
+    private static String getMessageDigestAlgoForChecksumKind(int type)
             throws NoSuchAlgorithmException {
-        switch (kind) {
-            case WHOLE_MD5:
+        switch (type) {
+            case TYPE_WHOLE_MD5:
                 return ALGO_MD5;
-            case WHOLE_SHA1:
+            case TYPE_WHOLE_SHA1:
                 return ALGO_SHA1;
-            case WHOLE_SHA256:
+            case TYPE_WHOLE_SHA256:
                 return ALGO_SHA256;
-            case WHOLE_SHA512:
+            case TYPE_WHOLE_SHA512:
                 return ALGO_SHA512;
             default:
-                throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind);
+                throw new NoSuchAlgorithmException("Invalid checksum type: " + type);
         }
     }
 
     private static void calculateChecksumIfRequested(Map<Integer, ApkChecksum> checksums,
-            String split, File file, int required, int kind) {
-        if ((required & kind) != 0 && !checksums.containsKey(kind)) {
-            final byte[] checksum = getApkChecksum(file, kind);
+            String split, File file, int required, int type) {
+        if ((required & type) != 0 && !checksums.containsKey(type)) {
+            final byte[] checksum = getApkChecksum(file, type);
             if (checksum != null) {
-                checksums.put(kind, new ApkChecksum(split, kind, checksum));
+                checksums.put(type, new ApkChecksum(split, type, checksum));
             }
         }
     }
 
-    private static byte[] getApkChecksum(File file, int kind) {
+    private static byte[] getApkChecksum(File file, int type) {
         try (FileInputStream fis = new FileInputStream(file);
              BufferedInputStream bis = new BufferedInputStream(fis)) {
             byte[] dataBytes = new byte[512 * 1024];
             int nread = 0;
 
-            final String algo = getMessageDigestAlgoForChecksumKind(kind);
+            final String algo = getMessageDigestAlgoForChecksumKind(type);
             MessageDigest md = MessageDigest.getInstance(algo);
             while ((nread = bis.read(dataBytes)) != -1) {
                 md.update(dataBytes, 0, nread);
@@ -624,9 +625,9 @@
     private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) {
         switch (contentDigestAlgo) {
             case CONTENT_DIGEST_CHUNKED_SHA256:
-                return PARTIAL_MERKLE_ROOT_1M_SHA256;
+                return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256;
             case CONTENT_DIGEST_CHUNKED_SHA512:
-                return PARTIAL_MERKLE_ROOT_1M_SHA512;
+                return TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512;
             default:
                 return -1;
         }
@@ -635,11 +636,11 @@
     private static void calculatePartialChecksumsIfRequested(Map<Integer, ApkChecksum> checksums,
             String split, File file, int required) {
         boolean needSignatureSha256 =
-                (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
-                        PARTIAL_MERKLE_ROOT_1M_SHA256);
+                (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
+                        TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256);
         boolean needSignatureSha512 =
-                (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
-                        PARTIAL_MERKLE_ROOT_1M_SHA512);
+                (required & TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
+                        TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512);
         if (!needSignatureSha256 && !needSignatureSha512) {
             return;
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2aafe9a..37fa9a2e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -28,6 +28,8 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
+import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageParser.APEX_FILE_EXTENSION;
 import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
 import static android.system.OsConstants.O_CREAT;
@@ -1458,7 +1460,7 @@
                     mChildSessionsRemaining.removeAt(sessionIndex);
                     if (mChildSessionsRemaining.size() == 0) {
                         destroyInternal();
-                        dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED,
+                        dispatchSessionFinished(INSTALL_SUCCEEDED,
                                 "Session installed", null);
                     }
                 } else if (PackageInstaller.STATUS_PENDING_USER_ACTION == status) {
@@ -1533,7 +1535,7 @@
 
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
-            assertPreparedAndNotDestroyedLocked("commit");
+            assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
             assertNoWriteFileTransfersOpenLocked();
 
             final boolean isSecureFrpEnabled =
@@ -1663,7 +1665,7 @@
             throws PackageManagerException {
         try {
             assertNoWriteFileTransfersOpenLocked();
-            assertPreparedAndNotDestroyedLocked("sealing of session");
+            assertPreparedAndNotDestroyedLocked("sealing of session " + sessionId);
             mSealed = true;
         } catch (Throwable e) {
             // Convert all exceptions into package manager exceptions as only those are handled
@@ -1701,6 +1703,12 @@
         }
     }
 
+    private void onSessionInstallationFailure(int error, String detailedMessage) {
+        Slog.e(TAG, "Install of session " + sessionId + " failed: " + detailedMessage);
+        destroyInternal();
+        dispatchSessionFinished(error, detailedMessage, null);
+    }
+
     private void onStorageHealthStatusChanged(int status) {
         final String packageName = getPackageName();
         if (TextUtils.isEmpty(packageName)) {
@@ -1757,15 +1765,18 @@
             try {
                 sealLocked();
 
-                if (isApexSession()) {
-                    // APEX installations rely on certain fields to be populated after reboot.
-                    // E.g. mPackageName.
-                    validateApexInstallLocked();
-                } else {
-                    // Populate mPackageName for this APK session which is required by the staging
-                    // manager to check duplicate apk-in-apex.
-                    PackageInstallerSession parent = allSessions.get(mParentSessionId);
-                    if (parent != null && parent.isStagedSessionReady()) {
+                // Session that are staged, ready and not multi package will be installed during
+                // this boot. As such, we need populate all the fields for successful installation.
+                if (isMultiPackage()) {
+                    return;
+                }
+                final PackageInstallerSession root = hasParentSessionId()
+                        ? allSessions.get(getParentSessionId())
+                        : this;
+                if (root != null && root.isStagedSessionReady()) {
+                    if (isApexSession()) {
+                        validateApexInstallLocked();
+                    } else {
                         validateApkInstallLocked();
                     }
                 }
@@ -1829,7 +1840,7 @@
             mStagingManager.commitSession(this);
             // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
             //  though ideally, we just need to send session committed broadcast.
-            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
+            dispatchSessionFinished(INSTALL_SUCCEEDED, "Session staged", null);
             return;
         }
 
@@ -1911,12 +1922,53 @@
         }
     }
 
+    /**
+     * Installs apks of staged session while skipping the verification process for a committed and
+     * ready session.
+     */
+    void installStagedSession(IntentSender statusReceiver) {
+        assertCallerIsOwnerOrRootOrSystemLocked();
+        Preconditions.checkArgument(!hasParentSessionId()); // Don't allow installing child sessions
+        Preconditions.checkArgument(isCommitted() && isStagedSessionReady());
+
+        // Since staged sessions are installed during boot, the original reference to status
+        // receiver from the owner has already been lost. We can safely replace it with a
+        // status receiver from the system without effecting the flow.
+        updateRemoteStatusReceiver(statusReceiver);
+        install();
+    }
+
+    private void updateRemoteStatusReceiver(IntentSender remoteStatusReceiver) {
+        synchronized (mLock) {
+            mRemoteStatusReceiver = remoteStatusReceiver;
+            if (isMultiPackage()) {
+                final IntentSender childIntentSender =
+                        new ChildStatusIntentReceiver(mChildSessions.clone(), remoteStatusReceiver)
+                                .getIntentSender();
+                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
+                    mChildSessions.valueAt(i).mRemoteStatusReceiver = childIntentSender;
+                }
+            }
+        }
+    }
+
+    private void install() {
+        try {
+            installNonStaged();
+        } catch (PackageManagerException e) {
+            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+            onSessionInstallationFailure(e.error, completeMsg);
+        }
+    }
+
     private void installNonStaged()
             throws PackageManagerException {
-        final PackageManagerService.InstallParams installingSession =
-                makeInstallParams();
+        Preconditions.checkArgument(containsApkSession());
+
+        final PackageManagerService.InstallParams installingSession = makeInstallParams();
         if (installingSession == null) {
-            return;
+            throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+                    "Session should contain at least one apk session for installation");
         }
         if (isMultiPackage()) {
             final List<PackageInstallerSession> childSessions;
@@ -2084,7 +2136,7 @@
                 @Override
                 public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                         Bundle extras) {
-                    if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                    if (returnCode == INSTALL_SUCCEEDED) {
                         onVerificationComplete();
                     } else {
                         onSessionVerificationFailure(returnCode, msg);
@@ -2126,20 +2178,14 @@
             return;
         }
 
-        try {
-            installNonStaged();
-        } catch (PackageManagerException e) {
-            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
-            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
-            destroyInternal();
-            dispatchSessionFinished(e.error, completeMsg, null);
-        }
+        install();
     }
 
     /**
      * Stages this session for install and returns a
      * {@link PackageManagerService.InstallParams} representing this new staged state.
      */
+    @Nullable
     private PackageManagerService.InstallParams makeInstallParams()
             throws PackageManagerException {
         synchronized (mLock) {
@@ -2153,8 +2199,13 @@
             }
         }
 
-        // We've reached point of no return; call into PMS to install the stage.
-        // Regardless of success or failure we always destroy session.
+        // Do not try to install apex session. Parent session will have at least one apk session.
+        if (!isMultiPackage() && isApexSession()) {
+            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
+                    "Apex package should have been installed by apexd", null);
+            return null;
+        }
+
         final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
             @Override
             public void onUserActionRequired(Intent intent) {
@@ -2164,8 +2215,14 @@
             @Override
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) {
-                destroyInternal();
-                dispatchSessionFinished(returnCode, msg, extras);
+                if (isStaged()) {
+                    sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
+                } else {
+                    // We've reached point of no return; call into PMS to install the stage.
+                    // Regardless of success or failure we always destroy session.
+                    destroyInternal();
+                    dispatchSessionFinished(returnCode, msg, extras);
+                }
             }
         };
 
@@ -2176,6 +2233,10 @@
             user = new UserHandle(userId);
         }
 
+        if (params.isStaged) {
+            params.installFlags |= INSTALL_STAGED;
+        }
+
         synchronized (mLock) {
             return mPm.new InstallParams(stageDir, localObserver, params, mInstallSource, user,
                     mSigningDetails, mInstallerUid);
@@ -2952,7 +3013,7 @@
             handle = NativeLibraryHelper.Handle.create(packageDir);
             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
                     abiOverride, isIncrementalInstallation());
-            if (res != PackageManager.INSTALL_SUCCEEDED) {
+            if (res != INSTALL_SUCCEEDED) {
                 throw new PackageManagerException(res,
                         "Failed to extract native libraries, res=" + res);
             }
@@ -3586,31 +3647,14 @@
     }
 
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
-        final IntentSender statusReceiver;
-        final String packageName;
+        sendUpdateToRemoteStatusReceiver(returnCode, msg, extras);
+
         synchronized (mLock) {
             mFinalStatus = returnCode;
             mFinalMessage = msg;
-
-            statusReceiver = mRemoteStatusReceiver;
-            packageName = mPackageName;
         }
 
-        if (statusReceiver != null) {
-            // Execute observer.onPackageInstalled on different thread as we don't want callers
-            // inside the system server have to worry about catching the callbacks while they are
-            // calling into the session
-            final SomeArgs args = SomeArgs.obtain();
-            args.arg1 = packageName;
-            args.arg2 = msg;
-            args.arg3 = extras;
-            args.arg4 = statusReceiver;
-            args.argi1 = returnCode;
-
-            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
-        }
-
-        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+        final boolean success = (returnCode == INSTALL_SUCCEEDED);
 
         // Send broadcast to default launcher only if it's a new install
         // TODO(b/144270665): Secure the usage of this broadcast.
@@ -3625,6 +3669,27 @@
         }
     }
 
+    private void sendUpdateToRemoteStatusReceiver(int returnCode, String msg, Bundle extras) {
+        final IntentSender statusReceiver;
+        final String packageName;
+        synchronized (mLock) {
+            statusReceiver = mRemoteStatusReceiver;
+            packageName = mPackageName;
+        }
+        if (statusReceiver != null) {
+            // Execute observer.onPackageInstalled on different thread as we don't want callers
+            // inside the system server have to worry about catching the callbacks while they are
+            // calling into the session
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = packageName;
+            args.arg2 = msg;
+            args.arg3 = extras;
+            args.arg4 = statusReceiver;
+            args.argi1 = returnCode;
+            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
+        }
+    }
+
     /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
@@ -3834,7 +3899,7 @@
     private static void sendOnPackageInstalled(Context context, IntentSender target, int sessionId,
             boolean showNotification, int userId, String basePackageName, int returnCode,
             String msg, Bundle extras) {
-        if (PackageManager.INSTALL_SUCCEEDED == returnCode && showNotification) {
+        if (INSTALL_SUCCEEDED == returnCode && showNotification) {
             boolean update = (extras != null) && extras.getBoolean(Intent.EXTRA_REPLACING);
             Notification notification = PackageInstallerService.buildSuccessNotification(context,
                     context.getResources()
@@ -4055,7 +4120,7 @@
                     CertifiedChecksum checksum = checksums.get(j);
                     out.startTag(null, TAG_SESSION_CHECKSUM);
                     writeStringAttribute(out, ATTR_NAME, fileName);
-                    writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getKind());
+                    writeIntAttribute(out, ATTR_CHECKSUM_KIND, checksum.getChecksum().getType());
                     writeByteArrayAttribute(out, ATTR_CHECKSUM_VALUE,
                             checksum.getChecksum().getValue());
                     writeStringAttribute(out, ATTR_CHECKSUM_PACKAGE, checksum.getPackageName());
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 54dbe2e..0f9a5cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20,10 +20,8 @@
 import static android.Manifest.permission.INSTALL_PACKAGES;
 import static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
 import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
-import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
-import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_IGNORED;
@@ -68,6 +66,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_RESTORE;
 import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
+import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
@@ -92,7 +91,6 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_LOCKED_USER;
 import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.RESTRICTION_NONE;
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
@@ -2470,9 +2468,9 @@
     }
 
     @Override
-    public void getChecksums(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Kind int optional,
-            @Checksum.Kind int required, @Nullable List trustedInstallers,
+    public void requestChecksums(@NonNull String packageName, boolean includeSplits,
+            @Checksum.Type int optional,
+            @Checksum.Type int required, @Nullable List trustedInstallers,
             @NonNull IntentSender statusReceiver, int userId) {
         Objects.requireNonNull(packageName);
         Objects.requireNonNull(statusReceiver);
@@ -14993,6 +14991,7 @@
         @Nullable MultiPackageInstallParams mParentInstallParams;
         final boolean forceQueryableOverride;
         final int mDataLoaderType;
+        final long requiredInstalledVersionCode;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, InstallSource installSource, String volumeUuid,
@@ -15013,6 +15012,7 @@
             this.installReason = PackageManager.INSTALL_REASON_UNKNOWN;
             this.forceQueryableOverride = false;
             this.mDataLoaderType = DataLoaderType.NONE;
+            this.requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
         }
 
         InstallParams(File stagedDir, IPackageInstallObserver2 observer,
@@ -15035,6 +15035,7 @@
             forceQueryableOverride = sessionParams.forceQueryableOverride;
             mDataLoaderType = (sessionParams.dataLoaderParams != null)
                     ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+            requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
         }
 
         @Override
@@ -15181,6 +15182,18 @@
         public void handleStartCopy() {
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     origin.resolvedPath, installFlags, packageAbiOverride);
+
+            // For staged session, there is a delay between its verification and install. Device
+            // state can change within this delay and hence we need to re-verify certain conditions.
+            boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
+            if (isStaged) {
+                mRet = verifyReplacingVersionCode(
+                        pkgLite, requiredInstalledVersionCode, installFlags);
+                if (mRet != INSTALL_SUCCEEDED) {
+                    return;
+                }
+            }
+
             mRet = overrideInstallLocation(pkgLite);
         }
 
@@ -15327,11 +15340,14 @@
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     origin.resolvedPath, installFlags, packageAbiOverride);
 
-            mRet = verifyReplacingVersionCode(pkgLite);
+            mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);
+            if (mRet != INSTALL_SUCCEEDED) {
+                return;
+            }
 
             // Perform package verification and enable rollback (unless we are simply moving the
             // package).
-            if (mRet == INSTALL_SUCCEEDED && !origin.existing) {
+            if (!origin.existing) {
                 sendApkVerificationRequest(pkgLite);
                 if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
                     sendEnableRollbackRequest();
@@ -15339,54 +15355,6 @@
             }
         }
 
-        private int verifyReplacingVersionCode(PackageInfoLite pkgLite) {
-            String packageName = pkgLite.packageName;
-            synchronized (mLock) {
-                // Package which currently owns the data that the new package will own if installed.
-                // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
-                // will be null whereas dataOwnerPkg will contain information about the package
-                // which was uninstalled while keeping its data.
-                AndroidPackage dataOwnerPkg = mPackages.get(packageName);
-                if (dataOwnerPkg  == null) {
-                    PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps != null) {
-                        dataOwnerPkg = ps.pkg;
-                    }
-                }
-
-                if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
-                    if (dataOwnerPkg == null) {
-                        Slog.w(TAG, "Required installed version code was "
-                                + requiredInstalledVersionCode
-                                + " but package is not installed");
-                        return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
-                    }
-
-                    if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
-                        Slog.w(TAG, "Required installed version code was "
-                                + requiredInstalledVersionCode
-                                + " but actual installed version is "
-                                + dataOwnerPkg.getLongVersionCode());
-                        return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
-                    }
-                }
-
-                if (dataOwnerPkg != null) {
-                    if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
-                            dataOwnerPkg.isDebuggable())) {
-                        try {
-                            checkDowngrade(dataOwnerPkg, pkgLite);
-                        } catch (PackageManagerException e) {
-                            Slog.w(TAG, "Downgrade detected: " + e.getMessage());
-                            return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-                        }
-                    }
-                }
-            }
-            return PackageManager.INSTALL_SUCCEEDED;
-        }
-
-
         void sendApkVerificationRequest(PackageInfoLite pkgLite) {
             final int verificationId = mPendingVerificationToken++;
 
@@ -15980,7 +15948,7 @@
                 return false;
             }
 
-            final File targetDir = codeFile.getParentFile();
+            final File targetDir = resolveTargetDir();
             final File beforeCodeFile = codeFile;
             final File afterCodeFile = getNextCodePath(targetDir, parsedPackage.getPackageName());
 
@@ -16023,6 +15991,17 @@
             return true;
         }
 
+        // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
+        //  flow, we won't need this method anymore.
+        private File resolveTargetDir() {
+            boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0;
+            if (isStagedInstall) {
+                return Environment.getDataAppDirectory(null);
+            } else {
+                return codeFile.getParentFile();
+            }
+        }
+
         int doPostInstall(int status, int uid) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 cleanUp();
@@ -21920,24 +21899,18 @@
 
         mInjector.getStorageManagerInternal().addExternalStoragePolicy(
                 new StorageManagerInternal.ExternalStorageMountPolicy() {
-            @Override
-            public int getMountMode(int uid, String packageName) {
-                if (Process.isIsolated(uid)) {
-                    return Zygote.MOUNT_EXTERNAL_NONE;
-                }
-                if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
-                    return Zygote.MOUNT_EXTERNAL_DEFAULT;
-                }
-                if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
-                    return Zygote.MOUNT_EXTERNAL_READ;
-                }
-                return Zygote.MOUNT_EXTERNAL_WRITE;
-            }
+                    @Override
+                    public int getMountMode(int uid, String packageName) {
+                        if (Process.isIsolated(uid)) {
+                            return Zygote.MOUNT_EXTERNAL_NONE;
+                        }
+                        return Zygote.MOUNT_EXTERNAL_DEFAULT;
+                    }
 
-            @Override
-            public boolean hasExternalStorage(int uid, String packageName) {
-                return true;
-            }
+                    @Override
+                    public boolean hasExternalStorage(int uid, String packageName) {
+                        return true;
+                    }
         });
 
         // Now that we're mostly running, clean up stale users and apps
@@ -24206,6 +24179,54 @@
         }
     }
 
+    private int verifyReplacingVersionCode(PackageInfoLite pkgLite,
+            long requiredInstalledVersionCode, int installFlags) {
+        String packageName = pkgLite.packageName;
+        synchronized (mLock) {
+            // Package which currently owns the data that the new package will own if installed.
+            // If an app is uninstalled while keeping data (e.g. adb uninstall -k), installedPkg
+            // will be null whereas dataOwnerPkg will contain information about the package
+            // which was uninstalled while keeping its data.
+            AndroidPackage dataOwnerPkg = mPackages.get(packageName);
+            if (dataOwnerPkg  == null) {
+                PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (ps != null) {
+                    dataOwnerPkg = ps.pkg;
+                }
+            }
+
+            if (requiredInstalledVersionCode != PackageManager.VERSION_CODE_HIGHEST) {
+                if (dataOwnerPkg == null) {
+                    Slog.w(TAG, "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but package is not installed");
+                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                }
+
+                if (dataOwnerPkg.getLongVersionCode() != requiredInstalledVersionCode) {
+                    Slog.w(TAG, "Required installed version code was "
+                            + requiredInstalledVersionCode
+                            + " but actual installed version is "
+                            + dataOwnerPkg.getLongVersionCode());
+                    return PackageManager.INSTALL_FAILED_WRONG_INSTALLED_VERSION;
+                }
+            }
+
+            if (dataOwnerPkg != null) {
+                if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
+                        dataOwnerPkg.isDebuggable())) {
+                    try {
+                        checkDowngrade(dataOwnerPkg, pkgLite);
+                    } catch (PackageManagerException e) {
+                        Slog.w(TAG, "Downgrade detected: " + e.getMessage());
+                        return PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
+                    }
+                }
+            }
+        }
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
     /**
      * Check and throw if the given before/after packages would be considered a
      * downgrade.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index afbf7d3..c356fc7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -96,6 +96,7 @@
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1450,7 +1451,7 @@
         final PrintWriter pw = getOutPrintWriter();
         final int parentSessionId = Integer.parseInt(getNextArg());
 
-        List<Integer> otherSessionIds = new ArrayList<>();
+        IntArray otherSessionIds = new IntArray();
         String opt;
         while ((opt = getNextArg()) != null) {
             otherSessionIds.add(Integer.parseInt(opt));
@@ -1459,7 +1460,7 @@
             pw.println("Error: At least two sessions are required.");
             return 1;
         }
-        return doInstallAddSession(parentSessionId, ArrayUtils.convertToIntArray(otherSessionIds),
+        return doInstallAddSession(parentSessionId, otherSessionIds.toArray(),
                 true /*logSuccess*/);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 2ff18f8..de9d3a0 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -28,7 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.pkg.PackageStateUnserialized;
 
 import java.io.File;
@@ -214,11 +214,12 @@
         mimeGroups = updatedMimeGroups;
     }
 
+    @Deprecated
     @Override
-    public AppIdPermissionState getPermissionsState() {
+    public LegacyPermissionState getLegacyPermissionState() {
         return (sharedUser != null)
-                ? sharedUser.getPermissionsState()
-                : super.getPermissionsState();
+                ? sharedUser.getLegacyPermissionState()
+                : super.getLegacyPermissionState();
     }
 
     public int getAppId() {
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index c1258b1..968c4b5 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,23 +19,29 @@
 import android.content.pm.ApplicationInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.LegacyPermissionState;
 
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public abstract class SettingBase {
     int pkgFlags;
     int pkgPrivateFlags;
 
-    protected final AppIdPermissionState mPermissionsState;
+    /**
+     * The legacy permission state that is read from package settings persistence for migration.
+     * This state here can not reflect the current permission state and should not be used for
+     * purposes other than migration.
+     */
+    @Deprecated
+    protected final LegacyPermissionState mLegacyPermissionsState;
 
     SettingBase(int pkgFlags, int pkgPrivateFlags) {
         setFlags(pkgFlags);
         setPrivateFlags(pkgPrivateFlags);
-        mPermissionsState = new AppIdPermissionState();
+        mLegacyPermissionsState = new LegacyPermissionState();
     }
 
     SettingBase(SettingBase orig) {
-        mPermissionsState = new AppIdPermissionState();
+        mLegacyPermissionsState = new LegacyPermissionState();
         doCopy(orig);
     }
 
@@ -46,11 +52,12 @@
     private void doCopy(SettingBase orig) {
         pkgFlags = orig.pkgFlags;
         pkgPrivateFlags = orig.pkgPrivateFlags;
-        mPermissionsState.copyFrom(orig.mPermissionsState);
+        mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
     }
 
-    public AppIdPermissionState getPermissionsState() {
-        return mPermissionsState;
+    @Deprecated
+    public LegacyPermissionState getLegacyPermissionState() {
+        return mLegacyPermissionsState;
     }
 
     void setFlags(int pkgFlags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c16bd5c..1dc035f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -107,10 +107,10 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.permission.AppIdPermissionState;
-import com.android.server.pm.permission.AppIdPermissionState.PermissionState;
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.LegacyPermissionDataProvider;
+import com.android.server.pm.permission.LegacyPermissionState;
+import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
 import com.android.server.pm.permission.PermissionSettings;
 import com.android.server.utils.TimingsTraceAndSlog;
 
@@ -733,7 +733,8 @@
                     pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures);
                     pkgSetting.appId = disabledPkg.appId;
                     // Clone permissions
-                    pkgSetting.getPermissionsState().copyFrom(disabledPkg.getPermissionsState());
+                    pkgSetting.getLegacyPermissionState()
+                            .copyFrom(disabledPkg.getLegacyPermissionState());
                     // Clone component info
                     List<UserInfo> users = getAllUsers(userManager);
                     if (users != null) {
@@ -2114,7 +2115,7 @@
     }
 
     void readInstallPermissionsLPr(XmlPullParser parser,
-            AppIdPermissionState permissionsState) throws IOException, XmlPullParserException {
+            LegacyPermissionState permissionsState) throws IOException, XmlPullParserException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2406,7 +2407,7 @@
                 serializer.attribute(null, "userId",
                         Integer.toString(usr.userId));
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                writePermissionsLPr(serializer, usr.getPermissionsState()
+                writePermissionsLPr(serializer, usr.getLegacyPermissionState()
                         .getInstallPermissionStates());
                 serializer.endTag(null, "shared-user");
             }
@@ -2734,7 +2735,7 @@
 
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState()
+            writePermissionsLPr(serializer, pkg.getLegacyPermissionState()
                     .getInstallPermissionStates());
         }
 
@@ -2825,7 +2826,8 @@
                     serializer, "install-initiator-sigs", mPastSignatures);
         }
 
-        writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissionStates());
+        writePermissionsLPr(serializer,
+                pkg.getLegacyPermissionState().getInstallPermissionStates());
 
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
@@ -3551,7 +3553,7 @@
             }
 
             if (parser.getName().equals(TAG_PERMISSIONS)) {
-                readInstallPermissionsLPr(parser, ps.getPermissionsState());
+                readInstallPermissionsLPr(parser, ps.getLegacyPermissionState());
             } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
                 readUsesStaticLibLPw(parser, ps);
             } else {
@@ -3848,7 +3850,7 @@
                     packageSetting.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals(TAG_PERMISSIONS)) {
                     readInstallPermissionsLPr(parser,
-                            packageSetting.getPermissionsState());
+                            packageSetting.getLegacyPermissionState());
                     packageSetting.installPermissionsFixed = true;
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
@@ -4074,7 +4076,7 @@
                 if (tagName.equals("sigs")) {
                     su.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals("perms")) {
-                    readInstallPermissionsLPr(parser, su.getPermissionsState());
+                    readInstallPermissionsLPr(parser, su.getLegacyPermissionState());
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <shared-user>: " + parser.getName());
@@ -4494,7 +4496,7 @@
 
     void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
             ArraySet<String> permissionNames, PackageSetting ps,
-            AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date,
+            LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date,
             List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
         AndroidPackage pkg = ps.pkg;
         if (checkinTag != null) {
@@ -4944,8 +4946,8 @@
                     && !packageName.equals(ps.name)) {
                 continue;
             }
-            final AppIdPermissionState permissionsState =
-                    mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+            final LegacyPermissionState permissionsState =
+                    mPermissionDataProvider.getLegacyPermissionState(ps.appId);
             if (permissionNames != null
                     && !permissionsState.hasPermissionState(permissionNames)) {
                 continue;
@@ -5002,8 +5004,8 @@
                     pw.println("Hidden system packages:");
                     printedSomething = true;
                 }
-                final AppIdPermissionState permissionsState =
-                        mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+                final LegacyPermissionState permissionsState =
+                        mPermissionDataProvider.getLegacyPermissionState(ps.appId);
                 dumpPackageLPr(pw, "  ", checkin ? "dis" : null, permissionNames, ps,
                         permissionsState, sdf, date, users, packageName != null, dumpAllComponents);
             }
@@ -5033,8 +5035,8 @@
             if (packageName != null && su != dumpState.getSharedUser()) {
                 continue;
             }
-            final AppIdPermissionState permissionsState =
-                    mPermissionDataProvider.getAppIdPermissionState(su.userId);
+            final LegacyPermissionState permissionsState =
+                    mPermissionDataProvider.getLegacyPermissionState(su.userId);
             if (permissionNames != null
                     && !permissionsState.hasPermissionState(permissionNames)) {
                 continue;
@@ -5178,7 +5180,7 @@
     }
 
     void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
-            AppIdPermissionState permissionsState) {
+            LegacyPermissionState permissionsState) {
         Collection<PermissionState> permissionStates =
                 permissionsState.getInstallPermissionStates();
         if (!permissionStates.isEmpty()) {
@@ -5424,7 +5426,7 @@
                 if (packageSetting.sharedUser == null) {
                     List<RuntimePermissionsState.PermissionState> permissions =
                             getPermissionsFromPermissionsState(
-                                    packageSetting.getPermissionsState(), userId);
+                                    packageSetting.getLegacyPermissionState(), userId);
                     packagePermissions.put(packageName, permissions);
                 }
             }
@@ -5437,7 +5439,7 @@
                 SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
                 List<RuntimePermissionsState.PermissionState> permissions =
                         getPermissionsFromPermissionsState(
-                                sharedUserSetting.getPermissionsState(), userId);
+                                sharedUserSetting.getLegacyPermissionState(), userId);
                 sharedUserPermissions.put(sharedUserName, permissions);
             }
 
@@ -5449,7 +5451,7 @@
 
         @NonNull
         private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
-                @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+                @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
             Collection<PermissionState> permissionStates =
                     permissionsState.getRuntimePermissionStates(userId);
             List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
@@ -5509,11 +5511,11 @@
                 List<RuntimePermissionsState.PermissionState> permissions =
                         packagePermissions.get(packageName);
                 if (permissions != null) {
-                    readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(),
+                    readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
                             userId);
                 } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
                     Slog.w(TAG, "Missing permission state for package: " + packageName);
-                    packageSetting.getPermissionsState().setMissing(true, userId);
+                    packageSetting.getLegacyPermissionState().setMissing(true, userId);
                 }
             }
 
@@ -5527,18 +5529,18 @@
                 List<RuntimePermissionsState.PermissionState> permissions =
                         sharedUserPermissions.get(sharedUserName);
                 if (permissions != null) {
-                    readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
-                            userId);
+                    readPermissionsStateLpr(permissions,
+                            sharedUserSetting.getLegacyPermissionState(), userId);
                 } else if (!isUpgradeToR) {
                     Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
-                    sharedUserSetting.getPermissionsState().setMissing(true, userId);
+                    sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
                 }
             }
         }
 
         private void readPermissionsStateLpr(
                 @NonNull List<RuntimePermissionsState.PermissionState> permissions,
-                @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+                @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
             int permissionsSize = permissions.size();
             for (int i = 0; i < permissionsSize; i++) {
                 RuntimePermissionsState.PermissionState permission = permissions.get(i);
@@ -5617,7 +5619,7 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
+                        parsePermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
                     } break;
 
                     case TAG_SHARED_USER: {
@@ -5628,14 +5630,14 @@
                             XmlUtils.skipCurrentTag(parser);
                             continue;
                         }
-                        parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
+                        parsePermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
                     } break;
                 }
             }
         }
 
         private void parsePermissionsLPr(XmlPullParser parser,
-                AppIdPermissionState permissionsState, int userId)
+                LegacyPermissionState permissionsState, int userId)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             int type;
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 35c26d6..529f16a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -794,18 +794,18 @@
 
     private void installApksInSession(PackageInstallerSession session)
             throws PackageManagerException {
-        final PackageInstallerSession apksToInstall = extractApksInSession(session);
-        if (apksToInstall == null) {
+        if (!session.containsApkSession()) {
             return;
         }
 
-        if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+        if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
             // If rollback is available for this session, notify the rollback
             // manager of the apk session so it can properly enable rollback.
             final RollbackManagerInternal rm =
                     LocalServices.getService(RollbackManagerInternal.class);
             try {
-                rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+                // TODO(b/136257624): extra apk session id in rollback is now redundant.
+                rm.notifyStagedApkSession(session.sessionId, session.sessionId);
             } catch (RuntimeException re) {
                 Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
                         + session.sessionId, re);
@@ -813,7 +813,7 @@
         }
 
         final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
-        apksToInstall.commit(receiver.getIntentSender(), false);
+        session.installStagedSession(receiver.getIntentSender());
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                 PackageInstaller.STATUS_FAILURE);
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
index 7452b52..346a2c5 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
@@ -31,7 +31,7 @@
      * @return the legacy permission state
      */
     @NonNull
-    public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId);
+    LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId);
 
     /**
      * Get the GIDs computed from the permission state of a UID, either a package or a shared user.
@@ -40,5 +40,5 @@
      * @return the GIDs for the UID
      */
     @NonNull
-    public abstract int[] getGidsForUid(int uid);
+    int[] getGidsForUid(int uid);
 }
diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
similarity index 97%
rename from services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
rename to services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
index aabdafd..63f69ce 100644
--- a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionState.java
@@ -32,7 +32,7 @@
  * Legacy permission state that was associated with packages or shared users.
  */
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public final class AppIdPermissionState {
+public final class LegacyPermissionState {
     // Maps from user IDs to user states.
     @NonNull
     private final SparseArray<UserState> mUserStates = new SparseArray<>();
@@ -48,7 +48,7 @@
      *
      * @hide
      */
-    public void copyFrom(@NonNull AppIdPermissionState other) {
+    public void copyFrom(@NonNull LegacyPermissionState other) {
         if (other == this) {
             return;
         }
@@ -88,7 +88,7 @@
         if (getClass() != object.getClass()) {
             return false;
         }
-        final AppIdPermissionState other = (AppIdPermissionState) object;
+        final LegacyPermissionState other = (LegacyPermissionState) object;
         return Objects.equals(mUserStates, other.mUserStates)
                 && Objects.equals(mMissing, other.mMissing);
     }
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 b293ba6..d7ff9a1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -68,7 +68,6 @@
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -104,7 +103,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
-import android.os.storage.StorageManagerInternal;
 import android.permission.IOnPermissionsChangeListener;
 import android.permission.IPermissionManager;
 import android.permission.PermissionControllerManager;
@@ -1554,24 +1552,6 @@
         if (bp.isRuntime()) {
             notifyRuntimePermissionStateChanged(packageName, userId);
         }
-
-        // Only need to do this if user is initialized. Otherwise it's a new user
-        // and there are no processes running as the user yet and there's no need
-        // to make an expensive call to remount processes for the changed permissions.
-        if (READ_EXTERNAL_STORAGE.equals(permName)
-                || WRITE_EXTERNAL_STORAGE.equals(permName)) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                if (mUserManagerInt.isUserInitialized(userId)) {
-                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                            StorageManagerInternal.class);
-                    storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
-
     }
 
     @Override
@@ -3551,9 +3531,9 @@
     private static boolean isProfileOwner(int uid) {
         DevicePolicyManagerInternal dpmInternal =
                 LocalServices.getService(DevicePolicyManagerInternal.class);
+        //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
         if (dpmInternal != null) {
-            return dpmInternal
-                    .isActiveAdminWithPolicy(uid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return dpmInternal.isActiveProfileOwner(uid) || dpmInternal.isActiveDeviceOwner(uid);
         }
         return false;
     }
@@ -4618,7 +4598,7 @@
         final int[] userIds = getAllUserIds();
         mPackageManagerInt.forEachPackageSetting(ps -> {
             final int appId = ps.getAppId();
-            final AppIdPermissionState appIdState = ps.getPermissionsState();
+            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
 
             synchronized (mLock) {
                 for (final int userId : userIds) {
@@ -4627,19 +4607,19 @@
                     userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
                     final UidPermissionState uidState = userState.getOrCreateUidState(appId);
                     uidState.reset();
-                    uidState.setMissing(appIdState.isMissing(userId));
+                    uidState.setMissing(legacyState.isMissing(userId));
                     readStateFromPermissionStates(uidState,
-                            appIdState.getInstallPermissionStates());
+                            legacyState.getInstallPermissionStates());
                     readStateFromPermissionStates(uidState,
-                            appIdState.getRuntimePermissionStates(userId));
+                            legacyState.getRuntimePermissionStates(userId));
                 }
             }
         });
     }
 
     private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
-            @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates) {
-        for (final AppIdPermissionState.PermissionState permissionState : permissionStates) {
+            @NonNull Collection<LegacyPermissionState.PermissionState> permissionStates) {
+        for (final LegacyPermissionState.PermissionState permissionState : permissionStates) {
             uidState.putPermissionState(permissionState.getPermission(),
                     permissionState.isGranted(), permissionState.getFlags());
         }
@@ -4649,8 +4629,8 @@
         final int[] userIds = mState.getUserIds();
         mPackageManagerInt.forEachPackageSetting(ps -> {
             ps.setInstallPermissionsFixed(false);
-            final AppIdPermissionState appIdState = ps.getPermissionsState();
-            appIdState.reset();
+            final LegacyPermissionState legacyState = ps.getLegacyPermissionState();
+            legacyState.reset();
             final int appId = ps.getAppId();
 
             synchronized (mLock) {
@@ -4672,21 +4652,21 @@
                         continue;
                     }
 
-                    appIdState.setMissing(uidState.isMissing(), userId);
+                    legacyState.setMissing(uidState.isMissing(), userId);
                     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 AppIdPermissionState.PermissionState legacyPermissionState =
-                                new AppIdPermissionState.PermissionState(
+                        final LegacyPermissionState.PermissionState legacyPermissionState =
+                                new LegacyPermissionState.PermissionState(
                                         permissionState.getPermission(),
                                         permissionState.isGranted(), permissionState.getFlags());
                         if (permissionState.isRuntime()) {
-                            appIdState.putRuntimePermissionState(legacyPermissionState,
+                            legacyState.putRuntimePermissionState(legacyPermissionState,
                                     userId);
                         } else {
-                            appIdState.putInstallPermissionState(legacyPermissionState);
+                            legacyState.putInstallPermissionState(legacyPermissionState);
                         }
                     }
                 }
@@ -4695,8 +4675,8 @@
     }
 
     @NonNull
-    private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
-        final AppIdPermissionState appIdState = new AppIdPermissionState();
+    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);
@@ -4711,17 +4691,17 @@
             for (int i = 0; i < permissionStatesSize; i++) {
                 final PermissionState permissionState = permissionStates.get(i);
 
-                final AppIdPermissionState.PermissionState legacyPermissionState =
-                        new AppIdPermissionState.PermissionState(permissionState.getPermission(),
+                final LegacyPermissionState.PermissionState legacyPermissionState =
+                        new LegacyPermissionState.PermissionState(permissionState.getPermission(),
                                 permissionState.isGranted(), permissionState.getFlags());
                 if (permissionState.isRuntime()) {
-                    appIdState.putRuntimePermissionState(legacyPermissionState, userId);
+                    legacyState.putRuntimePermissionState(legacyPermissionState, userId);
                 } else if (userId == UserHandle.USER_SYSTEM) {
-                    appIdState.putInstallPermissionState(legacyPermissionState);
+                    legacyState.putInstallPermissionState(legacyPermissionState);
                 }
             }
         }
-        return appIdState;
+        return legacyState;
     }
 
     @NonNull
@@ -5130,8 +5110,8 @@
         }
 
         @NonNull
-        public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
-            return PermissionManagerService.this.getAppIdPermissionState(appId);
+        public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) {
+            return PermissionManagerService.this.getLegacyPermissionState(appId);
         }
 
         @NonNull
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index cfceaabf..7b044ed 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -77,6 +77,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputChannel;
@@ -709,8 +710,7 @@
             }
             sessionState.isCurrent = false;
             sessionState.currentChannel = null;
-            notifyCurrentChannelInfosUpdatedLocked(
-                    userState, getCurrentTvChannelInfosInternalLocked(userState));
+            notifyCurrentChannelInfosUpdatedLocked(userState);
         } catch (RemoteException | SessionNotFoundException e) {
             Slog.e(TAG, "error in releaseSession", e);
         } finally {
@@ -851,15 +851,18 @@
         }
     }
 
-    private void notifyCurrentChannelInfosUpdatedLocked(
-            UserState userState, List<TvChannelInfo> infos) {
+    private void notifyCurrentChannelInfosUpdatedLocked(UserState userState) {
         if (DEBUG) {
             Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked");
         }
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
             try {
-                userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos);
+                ITvInputManagerCallback callback = userState.mCallbacks.getBroadcastItem(i);
+                Pair<Integer, Integer> pidUid = userState.callbackPidUidMap.get(callback);
+                List<TvChannelInfo> infos = getCurrentTvChannelInfosInternalLocked(
+                        userState, pidUid.first, pidUid.second);
+                callback.onCurrentTvChannelInfosUpdated(infos);
             } catch (RemoteException e) {
                 Slog.e(TAG, "failed to report updated current channel infos to callback", e);
             }
@@ -1063,14 +1066,19 @@
 
         @Override
         public void registerCallback(final ITvInputManagerCallback callback, int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "registerCallback");
+            int callingPid = Binder.getCallingPid();
+            int callingUid = Binder.getCallingUid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "registerCallback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     if (!userState.mCallbacks.register(callback)) {
                         Slog.e(TAG, "client process has already died");
+                    } else {
+                        userState.callbackPidUidMap.put(
+                                callback, Pair.create(callingPid, callingUid));
                     }
                 }
             } finally {
@@ -1087,6 +1095,7 @@
                 synchronized (mLock) {
                     UserState userState = getOrCreateUserStateLocked(resolvedUserId);
                     userState.mCallbacks.unregister(callback);
+                    userState.callbackPidUidMap.remove(callback);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -1419,8 +1428,8 @@
         @Override
         public void tune(IBinder sessionToken, final Uri channelUri, Bundle params, int userId) {
             final int callingUid = Binder.getCallingUid();
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
-                    userId, "tune");
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId, "tune");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
@@ -1432,8 +1441,7 @@
                         if (sessionState != null) {
                             sessionState.isCurrent = true;
                             sessionState.currentChannel = channelUri;
-                            notifyCurrentChannelInfosUpdatedLocked(
-                                    userState, getCurrentTvChannelInfosInternalLocked(userState));
+                            notifyCurrentChannelInfosUpdatedLocked(userState);
                         }
                         if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
                             // Do not log the watch history for passthrough inputs.
@@ -2090,16 +2098,13 @@
 
         @Override
         public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "getTvCurrentChannelInfos");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getOrCreateUserStateLocked(resolvedUserId);
-                    return getCurrentTvChannelInfosInternalLocked(userState);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+            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);
             }
         }
 
@@ -2273,14 +2278,15 @@
         }
     }
 
-    private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) {
+    private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(
+            UserState userState, int callingPid, int callingUid) {
         List<TvChannelInfo> channelInfos = new ArrayList<>();
-        boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission();
+        boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission(callingPid, callingUid);
         for (SessionState state : userState.sessionStateMap.values()) {
             if (state.isCurrent) {
                 Integer appTag;
                 int appType;
-                if (state.callingUid == Binder.getCallingUid()) {
+                if (state.callingUid == callingUid) {
                     appTag = APP_TAG_SELF;
                     appType = TvChannelInfo.APP_TYPE_SELF;
                 } else {
@@ -2322,8 +2328,8 @@
         return false;
     }
 
-    private boolean hasAccessWatchedProgramsPermission() {
-        return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
+    private boolean hasAccessWatchedProgramsPermission(int callingPid, int callingUid) {
+        return mContext.checkPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS, callingPid, callingUid)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
@@ -2360,6 +2366,9 @@
         private final RemoteCallbackList<ITvInputManagerCallback> mCallbacks =
                 new RemoteCallbackList<ITvInputManagerCallback>();
 
+        private final Map<ITvInputManagerCallback, Pair<Integer, Integer>> callbackPidUidMap =
+                new HashMap<>();
+
         // The token of a "main" TV input session.
         private IBinder mainSessionToken = null;
 
@@ -2712,8 +2721,7 @@
                 mSessionState.isCurrent = true;
                 mSessionState.currentChannel = channelUri;
                 UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
-                notifyCurrentChannelInfosUpdatedLocked(
-                        userState, getCurrentTvChannelInfosInternalLocked(userState));
+                notifyCurrentChannelInfosUpdatedLocked(userState);
                 try {
                     // TODO: Consider adding this channel change in the watch log. When we do
                     // that, how we can protect the watch log from malicious tv inputs should
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index e2c1303..45d3fce 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1468,13 +1468,13 @@
         }
     }
 
-    private void removeStackInSurfaceTransaction(Task stack) {
-        if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) {
-            removePinnedStackInSurfaceTransaction(stack);
+    private void removeRootTaskInSurfaceTransaction(Task rootTask) {
+        if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
+            removePinnedStackInSurfaceTransaction(rootTask);
         } else {
             final PooledConsumer c = PooledLambda.obtainConsumer(
                     ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
-            stack.forAllLeafTasks(c, true /* traverseTopToBottom */);
+            rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
             c.recycle();
         }
     }
@@ -1484,12 +1484,12 @@
     }
 
     /**
-     * Removes the stack associated with the given {@param stack}. If the {@param stack} is the
-     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
-     * instead moved back onto the fullscreen stack.
+     * Removes the root task associated with the given {@param task}. If the {@param task} is the
+     * pinned task, then its child tasks are not explicitly removed when the root task is
+     * destroyed, but instead moved back onto the TaskDisplayArea.
      */
-    void removeStack(Task stack) {
-        mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stack));
+    void removeRootTask(Task task) {
+        mWindowManager.inSurfaceTransaction(() -> removeRootTaskInSurfaceTransaction(task));
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e65be41..eaa5839 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -283,7 +283,7 @@
     /**
      * Cancels any currently running recents animation.
      */
-    public abstract void cancelRecentsAnimation(boolean restoreHomeStackPosition);
+    public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
 
     /**
      * This enforces {@code func} can only be called if either the caller is Recents activity or
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f1529bd..e06c156 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1585,10 +1585,11 @@
      * Start the recents activity to perform the recents animation.
      *
      * @param intent                 The intent to start the recents activity.
+     * @param eventTime              When the (touch) event is triggered to start recents activity.
      * @param recentsAnimationRunner Pass {@code null} to only preload the activity.
      */
     @Override
-    public void startRecentsActivity(Intent intent, @Deprecated IAssistDataReceiver unused,
+    public void startRecentsActivity(Intent intent, long eventTime,
             @Nullable IRecentsAnimationRunner recentsAnimationRunner) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
         final int callingPid = Binder.getCallingPid();
@@ -1608,7 +1609,7 @@
                 if (recentsAnimationRunner == null) {
                     anim.preloadRecentsActivity();
                 } else {
-                    anim.startRecentsActivity(recentsAnimationRunner);
+                    anim.startRecentsActivity(recentsAnimationRunner, eventTime);
                 }
             }
         } finally {
@@ -2186,19 +2187,19 @@
     }
 
     @Override
-    public void setFocusedStack(int stackId) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId);
+    public void setFocusedRootTask(int taskId) {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedRootTask()");
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedRootTask: taskId=%d", taskId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
+                final Task task = mRootWindowContainer.getStack(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "setFocusedRootTask: No task with id=" + taskId);
                     return;
                 }
-                final ActivityRecord r = stack.topRunningActivity();
-                if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) {
+                final ActivityRecord r = task.topRunningActivity();
+                if (r != null && r.moveFocusableActivityToTop("setFocusedRootTask")) {
                     mRootWindowContainer.resumeFocusedStacksTopActivities();
                 }
             }
@@ -2252,8 +2253,19 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return mStackSupervisor.removeTaskById(taskId, true, REMOVE_FROM_RECENTS,
-                        "remove-task");
+                final Task task = mRootWindowContainer.anyTaskForId(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
+                    return false;
+                }
+
+                if (task.isLeafTask()) {
+                    mStackSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
+                } else {
+                    mStackSupervisor.removeRootTask(task);
+                }
+                return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2749,31 +2761,31 @@
     }
 
     @Override
-    public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
+    public void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToRootTask()");
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
                 final Task task = mRootWindowContainer.anyTaskForId(taskId);
                 if (task == null) {
-                    Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId);
+                    Slog.w(TAG, "moveTaskToRootTask: No task for id=" + taskId);
                     return;
                 }
 
-                ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToStack: moving task=%d to "
-                        + "stackId=%d toTop=%b", taskId, stackId, toTop);
+                ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToRootTask: moving task=%d to "
+                        + "rootTaskId=%d toTop=%b", taskId, rootTaskId, toTop);
 
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
+                final Task rootTask = mRootWindowContainer.getStack(rootTaskId);
+                if (rootTask == null) {
                     throw new IllegalStateException(
-                            "moveTaskToStack: No stack for stackId=" + stackId);
+                            "moveTaskToRootTask: No rootTask for rootTaskId=" + rootTaskId);
                 }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
-                            + taskId + " to stack " + stackId);
+                if (!rootTask.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("moveTaskToRootTask: Attempt to move task "
+                            + taskId + " to rootTask " + rootTaskId);
                 }
-                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
-                        "moveTaskToStack");
+                task.reparent(rootTask, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+                        "moveTaskToRootTask");
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2861,18 +2873,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
     @Override
-    public void removeStacksInWindowingModes(int[] windowingModes) {
+    public void removeRootTasksInWindowingModes(int[] windowingModes) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksInWindowingModes()");
+                "removeRootTasksInWindowingModes()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mRootWindowContainer.removeStacksInWindowingModes(windowingModes);
+                mRootWindowContainer.removeRootTasksInWindowingModes(windowingModes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2880,14 +2892,14 @@
     }
 
     @Override
-    public void removeStacksWithActivityTypes(int[] activityTypes) {
+    public void removeRootTasksWithActivityTypes(int[] activityTypes) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "removeStacksWithActivityTypes()");
+                "removeRootTasksWithActivityTypes()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                mRootWindowContainer.removeStacksWithActivityTypes(activityTypes);
+                mRootWindowContainer.removeRootTasksWithActivityTypes(activityTypes);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2961,14 +2973,14 @@
     }
 
     @Override
-    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+    public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
         final long callingUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
+                mWindowManager.cancelRecentsAnimation(restoreHomeRootTaskPosition
                         ? REORDER_MOVE_TO_ORIGINAL_POSITION
                         : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
             }
@@ -3046,7 +3058,7 @@
         long ident = Binder.clearCallingIdentity();
         try {
             // When a task is locked, dismiss the pinned stack if it exists
-            mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+            mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
             getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
         } finally {
@@ -3468,37 +3480,15 @@
     }
 
     @Override
-    public void removeStack(int stackId) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                final Task stack = mRootWindowContainer.getStack(stackId);
-                if (stack == null) {
-                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
-                    return;
-                }
-                if (!stack.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException(
-                            "Removing non-standard stack is not allowed.");
-                }
-                mStackSupervisor.removeStack(stack);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    @Override
-    public void moveStackToDisplay(int stackId, int displayId) {
-        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+    public void moveRootTaskToDisplay(int taskId, int displayId) {
+        mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveRootTaskToDisplay()");
 
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                ProtoLog.d(WM_DEBUG_TASKS, "moveStackToDisplay: moving stackId=%d to "
-                        + "displayId=%d", stackId, displayId);
-                mRootWindowContainer.moveStackToDisplay(stackId, displayId, ON_TOP);
+                ProtoLog.d(WM_DEBUG_TASKS, "moveRootTaskToDisplay: moving taskId=%d to "
+                        + "displayId=%d", taskId, displayId);
+                mRootWindowContainer.moveStackToDisplay(taskId, displayId, ON_TOP);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -3992,26 +3982,26 @@
     }
 
     /**
-     * Moves the top activity in the input stackId to the pinned stack.
+     * Moves the top activity in the input rootTaskId to the pinned root task.
      *
-     * @param stackId Id of stack to move the top activity to pinned stack.
-     * @param bounds  Bounds to use for pinned stack.
-     * @return True if the top activity of the input stack was successfully moved to the pinned
-     * stack.
+     * @param rootTaskId Id of root task to move the top activity to pinned root task.
+     * @param bounds     Bounds to use for pinned root task.
+     * @return True if the top activity of the input stack was successfully moved to the pinned root
+     * task.
      */
     @Override
-    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+    public boolean moveTopActivityToPinnedRootTask(int rootTaskId, Rect bounds) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
-                "moveTopActivityToPinnedStack()");
+                "moveTopActivityToPinnedRootTask()");
         synchronized (mGlobalLock) {
             if (!mSupportsPictureInPicture) {
-                throw new IllegalStateException("moveTopActivityToPinnedStack:"
+                throw new IllegalStateException("moveTopActivityToPinnedRootTask:"
                         + "Device doesn't support picture-in-picture mode");
             }
 
             long ident = Binder.clearCallingIdentity();
             try {
-                return mRootWindowContainer.moveTopStackActivityToPinnedStack(stackId);
+                return mRootWindowContainer.moveTopStackActivityToPinnedRootTask(rootTaskId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -4183,10 +4173,10 @@
 
     // TODO(b/149338177): remove when CTS no-longer requires it
     @Override
-    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+    public void resizePrimarySplitScreen(Rect dockedBounds, Rect tempDockedTaskBounds,
             Rect tempDockedTaskInsetBounds,
             Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePrimarySplitScreen()");
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -4709,7 +4699,7 @@
                 if (disableNonVrUi) {
                     // If we are in a VR mode where Picture-in-Picture mode is unsupported,
                     // then remove the pinned stack.
-                    mRootWindowContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+                    mRootWindowContainer.removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
                 }
             }
         });
@@ -6285,8 +6275,8 @@
         }
 
         @Override
-        public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
-            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeStackPosition);
+        public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
+            ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/DisplayAreaGroup.java b/services/core/java/com/android/server/wm/DisplayAreaGroup.java
new file mode 100644
index 0000000..bcf8c7c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaGroup.java
@@ -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 com.android.server.wm;
+
+import static android.content.pm.ActivityInfo.reverseOrientation;
+
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+
+/** The root of a partition of the logical display. */
+class DisplayAreaGroup extends RootDisplayArea {
+
+    DisplayAreaGroup(WindowManagerService wms, String name, int featureId) {
+        super(wms, name, featureId);
+    }
+
+    @Override
+    boolean isOrientationDifferentFromDisplay() {
+        if (mDisplayContent == null) {
+            return false;
+        }
+
+        final Rect bounds = getBounds();
+        final Rect displayBounds = mDisplayContent.getBounds();
+
+        return (bounds.width() < bounds.height())
+                != (displayBounds.width() < displayBounds.height());
+    }
+
+    @ActivityInfo.ScreenOrientation
+    @Override
+    int getOrientation(int candidate) {
+        int orientation = super.getOrientation(candidate);
+
+        // Reverse the requested orientation if the orientation of this DAG is different from the
+        // display, so that when the display rotates to the reversed orientation, this DAG will be
+        // in the requested orientation, so as the requested app.
+        // For example, if the display is 1200x900 (landscape), and this DAG is 600x900 (portrait).
+        // When an app below this DAG is requesting landscape, it should actually request the
+        // display to be portrait, so that the DAG and the app will be in landscape.
+        return isOrientationDifferentFromDisplay() ? reverseOrientation(orientation) : orientation;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 22b446d..4b2c41d 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4929,18 +4929,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            taskDisplayArea.removeStacksInWindowingModes(windowingModes);
+            taskDisplayArea.removeRootTasksInWindowingModes(windowingModes);
         });
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         forAllTaskDisplayAreas(taskDisplayArea -> {
-            taskDisplayArea.removeStacksWithActivityTypes(activityTypes);
+            taskDisplayArea.removeRootTasksWithActivityTypes(activityTypes);
         });
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index acf5f75..803a533 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -285,10 +285,12 @@
         inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
         inputWindowHandle.focusable = focusable;
+        inputWindowHandle.touchOcclusionMode = child.getTouchOcclusionMode();
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
         inputWindowHandle.ownerPid = child.mSession.mPid;
         inputWindowHandle.ownerUid = child.mSession.mUid;
+        inputWindowHandle.packageName = child.getOwningPackage();
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
         inputWindowHandle.displayId = child.getDisplayId();
 
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 56e1187..a0074a2 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -144,11 +144,11 @@
                         mTmpParams.mPreferredTaskDisplayArea, true /* onTop */);
             }
 
-            if (mTmpParams.hasWindowingMode()
-                    && mTmpParams.mWindowingMode != task.getRootTask().getWindowingMode()) {
+            if (mTmpParams.hasWindowingMode() && task.isRootTask()
+                    && mTmpParams.mWindowingMode != task.getWindowingMode()) {
                 final int activityType = activity != null
                         ? activity.getActivityType() : task.getActivityType();
-                task.getRootTask().setWindowingMode(task.getDisplayArea().validateWindowingMode(
+                task.setWindowingMode(task.getDisplayArea().validateWindowingMode(
                         mTmpParams.mWindowingMode, activity, task, activityType));
             }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 35338bb..1cf50ab 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -161,7 +161,7 @@
         }
     }
 
-    void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
+    void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) {
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
 
@@ -249,8 +249,13 @@
             // we fetch the visible tasks to be controlled by the animation
             mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
 
+            ActivityOptions options = null;
+            if (eventTime > 0) {
+                options = ActivityOptions.makeBasic();
+                options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
+            }
             mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState,
-                    START_TASK_TO_FRONT, targetActivity, null /* options */);
+                    START_TASK_TO_FRONT, targetActivity, options);
 
             // Register for stack order changes
             mDefaultTaskDisplayArea.registerStackOrderChangedListener(this);
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index faed7fa..d9a8773 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -27,7 +27,8 @@
 
 /**
  * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
- * of the whole logical display, or the root of a {@link DisplayArea} group.
+ * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
+ * logical display.
  */
 class RootDisplayArea extends DisplayArea<DisplayArea> {
 
@@ -50,6 +51,16 @@
         super(wms, Type.ANY, name, featureId);
     }
 
+    @Override
+    RootDisplayArea getRootDisplayArea() {
+        return this;
+    }
+
+    /** Whether the orientation (based on dimensions) of this root is different from the Display. */
+    boolean isOrientationDifferentFromDisplay() {
+        return false;
+    }
+
     /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */
     DisplayArea.Tokens findAreaForToken(WindowToken token) {
         int windowLayerFromType = token.getWindowLayerFromType();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index cabf1bf..7e76e75 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2038,7 +2038,7 @@
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
         // also cause all tasks to be moved to the fullscreen stack at a position that is
         // appropriate.
-        removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+        removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
         mCurrentUser = userId;
@@ -2141,27 +2141,27 @@
         moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop);
     }
 
-    boolean moveTopStackActivityToPinnedStack(int stackId) {
-        final Task stack = getStack(stackId);
-        if (stack == null) {
+    boolean moveTopStackActivityToPinnedRootTask(int rootTaskId) {
+        final Task rootTask = getStack(rootTaskId);
+        if (rootTask == null) {
             throw new IllegalArgumentException(
-                    "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId);
+                    "moveTopStackActivityToPinnedRootTask: Unknown rootTaskId=" + rootTaskId);
         }
 
-        final ActivityRecord r = stack.topRunningActivity();
+        final ActivityRecord r = rootTask.topRunningActivity();
         if (r == null) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity"
-                    + " in stack=" + stack);
+            Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: No top running activity"
+                    + " in rootTask=" + rootTask);
             return false;
         }
 
         if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) {
-            Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for "
-                    + " r=" + r);
+            Slog.w(TAG, "moveTopStackActivityToPinnedRootTask: Picture-In-Picture not supported "
+                    + "for r=" + r);
             return false;
         }
 
-        moveActivityToPinnedStack(r, "moveTopActivityToPinnedStack");
+        moveActivityToPinnedStack(r, "moveTopStackActivityToPinnedRootTask");
         return true;
     }
 
@@ -3310,18 +3310,18 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            getChildAt(i).removeStacksInWindowingModes(windowingModes);
+            getChildAt(i).removeRootTasksInWindowingModes(windowingModes);
         }
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         for (int i = getChildCount() - 1; i >= 0; --i) {
-            getChildAt(i).removeStacksWithActivityTypes(activityTypes);
+            getChildAt(i).removeRootTasksWithActivityTypes(activityTypes);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f984576..249fe03 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6911,7 +6911,7 @@
         moveToBack("moveTaskToBackLocked", tr);
 
         if (inPinnedWindowingMode()) {
-            mStackSupervisor.removeStack(this);
+            mStackSupervisor.removeRootTask(this);
             return true;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 696e1ca..d69fb0b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1289,66 +1289,65 @@
     }
 
     /**
-     * Removes stacks in the input windowing modes from the system if they are of activity type
+     * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
-    void removeStacksInWindowingModes(int... windowingModes) {
+    void removeRootTasksInWindowingModes(int... windowingModes) {
         if (windowingModes == null || windowingModes.length == 0) {
             return;
         }
 
-        // Collect the stacks that are necessary to be removed instead of performing the removal
-        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
-        // stacks reordered.
-        final ArrayList<Task> stacks = new ArrayList<>();
+        // Collect the root tasks that are necessary to be removed instead of performing the removal
+        // by looping the children, so that we don't miss any root tasks after the children size
+        // changed or reordered.
+        final ArrayList<Task> rootTasks = new ArrayList<>();
         for (int j = windowingModes.length - 1; j >= 0; --j) {
             final int windowingMode = windowingModes[j];
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final Task stack = getStackAt(i);
-                if (!stack.isActivityTypeStandardOrUndefined()) {
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task rootTask = mChildren.get(i);
+                if (rootTask.mCreatedByOrganizer
+                        || !rootTask.isActivityTypeStandardOrUndefined()
+                        || rootTask.getWindowingMode() != windowingMode) {
                     continue;
                 }
-                if (stack.getWindowingMode() != windowingMode) {
-                    continue;
-                }
-                stacks.add(stack);
+                rootTasks.add(rootTask);
             }
         }
 
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+        for (int i = rootTasks.size() - 1; i >= 0; --i) {
+            mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i));
         }
     }
 
-    void removeStacksWithActivityTypes(int... activityTypes) {
+    void removeRootTasksWithActivityTypes(int... activityTypes) {
         if (activityTypes == null || activityTypes.length == 0) {
             return;
         }
 
-        // Collect the stacks that are necessary to be removed instead of performing the removal
-        // by looping mStacks, so that we don't miss any stacks after the stack size changed or
-        // stacks reordered.
-        final ArrayList<Task> stacks = new ArrayList<>();
+        // Collect the root tasks that are necessary to be removed instead of performing the removal
+        // by looping the children, so that we don't miss any root tasks after the children size
+        // changed or reordered.
+        final ArrayList<Task> rootTasks = new ArrayList<>();
         for (int j = activityTypes.length - 1; j >= 0; --j) {
             final int activityType = activityTypes[j];
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final Task stack = getStackAt(i);
+            for (int i = mChildren.size() - 1; i >= 0; --i) {
+                final Task rootTask = mChildren.get(i);
                 // Collect the root tasks that are currently being organized.
-                if (stack.mCreatedByOrganizer) {
-                    for (int k = stack.getChildCount() - 1; k >= 0; --k) {
-                        final Task childStack = (Task) stack.getChildAt(k);
-                        if (childStack.getActivityType() == activityType) {
-                            stacks.add(childStack);
+                if (rootTask.mCreatedByOrganizer) {
+                    for (int k = rootTask.getChildCount() - 1; k >= 0; --k) {
+                        final Task task = (Task) rootTask.getChildAt(k);
+                        if (task.getActivityType() == activityType) {
+                            rootTasks.add(task);
                         }
                     }
-                } else if (stack.getActivityType() == activityType) {
-                    stacks.add(stack);
+                } else if (rootTask.getActivityType() == activityType) {
+                    rootTasks.add(rootTask);
                 }
             }
         }
 
-        for (int i = stacks.size() - 1; i >= 0; --i) {
-            mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+        for (int i = rootTasks.size() - 1; i >= 0; --i) {
+            mRootWindowContainer.mStackSupervisor.removeRootTask(rootTasks.get(i));
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 2ece30d..38ec924 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -22,6 +22,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
+import static android.content.pm.ActivityInfo.reverseOrientation;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -809,6 +810,13 @@
         return parent != null ? parent.getDisplayArea() : null;
     }
 
+    /** Get the first node of type {@link RootDisplayArea} above or at this node. */
+    @Nullable
+    RootDisplayArea getRootDisplayArea() {
+        WindowContainer parent = getParent();
+        return parent != null ? parent.getRootDisplayArea() : null;
+    }
+
     boolean isAttached() {
         return getDisplayArea() != null;
     }
@@ -1154,17 +1162,30 @@
      *         {@link Configuration#ORIENTATION_UNDEFINED}).
      */
     int getRequestedConfigurationOrientation() {
-        if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+        int requestedOrientation = mOrientation;
+        final RootDisplayArea root = getRootDisplayArea();
+        if (root != null && root.isOrientationDifferentFromDisplay()) {
+            // Reverse the requested orientation if the orientation of its root is different from
+            // the display, so that when the display rotates to the reversed orientation, the
+            // requested app will be in the requested orientation.
+            // For example, if the display is 1200x900 (landscape), and the DAG is 600x900
+            // (portrait).
+            // When an app below the DAG is requesting landscape, it should actually request the
+            // display to be portrait, so that the DAG and the app will be in landscape.
+            requestedOrientation = reverseOrientation(mOrientation);
+        }
+
+        if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
             // NOSENSOR means the display's "natural" orientation, so return that.
             if (mDisplayContent != null) {
                 return mDisplayContent.getNaturalOrientation();
             }
-        } else if (mOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+        } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
             // LOCKED means the activity's orientation remains unchanged, so return existing value.
             return getConfiguration().orientation;
-        } else if (isFixedOrientationLandscape(mOrientation)) {
+        } else if (isFixedOrientationLandscape(requestedOrientation)) {
             return ORIENTATION_LANDSCAPE;
-        } else if (isFixedOrientationPortrait(mOrientation)) {
+        } else if (isFixedOrientationPortrait(requestedOrientation)) {
             return ORIENTATION_PORTRAIT;
         }
         return ORIENTATION_UNDEFINED;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 8f42b3f..5defe478 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.graphics.GraphicsProtos.dumpPointProto;
+import static android.hardware.input.InputManager.BLOCK_UNTRUSTED_TOUCHES;
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -182,6 +183,7 @@
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
+import android.app.compat.CompatChanges;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Matrix;
@@ -198,6 +200,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.TouchOcclusionMode;
 import android.os.Trace;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -434,7 +437,7 @@
 
     /**
      * Usually empty. Set to the task's tempInsetFrame. See
-     *{@link android.app.IActivityTaskManager#resizeDockedStack}.
+     *{@link android.app.IActivityTaskManager#resizePrimarySplitScreen}.
      */
     private final Rect mInsetFrame = new Rect();
 
@@ -958,6 +961,16 @@
                 : service.mAtmService.getProcessController(s.mPid, s.mUid);
     }
 
+    int getTouchOcclusionMode() {
+        if (!CompatChanges.isChangeEnabled(BLOCK_UNTRUSTED_TOUCHES, mOwnerUid)) {
+            return TouchOcclusionMode.ALLOW;
+        }
+        if (WindowManager.LayoutParams.isSystemAlertWindowType(mAttrs.type)) {
+            return TouchOcclusionMode.USE_OPACITY;
+        }
+        return TouchOcclusionMode.BLOCK_UNTRUSTED;
+    }
+
     void attach() {
         if (DEBUG) Slog.v(TAG, "Attaching " + this + " token=" + mToken);
         mSession.windowAddedLocked(mAttrs.packageName);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9f653d6..13d8dc4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,12 +23,10 @@
 import static android.graphics.Matrix.MTRANS_X;
 import static android.graphics.Matrix.MTRANS_Y;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DRAW;
@@ -46,19 +44,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_CROP;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.logWithStack;
 import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
-import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT;
 import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
 import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 
-import android.app.WindowConfiguration;
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
@@ -365,7 +360,7 @@
             if (mSurfaceController != null && mPendingDestroySurface != null) {
                 mPostDrawTransaction.reparentChildren(
                     mSurfaceController.getClientViewRootSurface(),
-                    mPendingDestroySurface.mSurfaceControl).apply();
+                    mPendingDestroySurface.getClientViewRootSurface()).apply();
             }
             destroySurfaceLocked();
             mSurfaceDestroyDeferred = true;
@@ -399,7 +394,7 @@
                 && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
             mPostDrawTransaction.reparentChildren(
                     mPendingDestroySurface.getClientViewRootSurface(),
-                    mSurfaceController.mSurfaceControl).apply();
+                    mSurfaceController.getClientViewRootSurface()).apply();
         }
 
         destroyDeferredSurfaceLocked();
@@ -993,7 +988,7 @@
             if (!mPendingDestroySurface.mChildrenDetached) {
                 mPostDrawTransaction.reparentChildren(
                         mPendingDestroySurface.getClientViewRootSurface(),
-                        mSurfaceController.mSurfaceControl);
+                        mSurfaceController.getClientViewRootSurface());
             }
         }
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e39a3d1..d14780e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1453,6 +1453,21 @@
     im->getInputManager()->getDispatcher()->setInTouchMode(inTouchMode);
 }
 
+static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */,
+                                                     jlong ptr, jfloat opacity) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getDispatcher()->setMaximumObscuringOpacityForTouch(opacity);
+}
+
+static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                               jint mode) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->getInputManager()->getDispatcher()->setBlockUntrustedTouchesMode(
+            static_cast<BlockUntrustedTouchesMode>(mode));
+}
+
 static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
         jlong ptr, jobject inputEventObj, jint injectorPid, jint injectorUid,
         jint syncMode, jint timeoutMillis, jint policyFlags) {
@@ -1790,6 +1805,9 @@
         {"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
         {"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
         {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
+        {"nativeSetMaximumObscuringOpacityForTouch", "(JF)V",
+         (void*)nativeSetMaximumObscuringOpacityForTouch},
+        {"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode},
         {"nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I",
          (void*)nativeInjectInputEvent},
         {"nativeVerifyInputEvent", "(JLandroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;",
diff --git a/services/core/xsd/vts/Android.mk b/services/core/xsd/vts/Android.mk
deleted file mode 100644
index 6dc2c43..0000000
--- a/services/core/xsd/vts/Android.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := VtsValidateDefaultPermissions
-include test/vts/tools/build/Android.host_config.mk
diff --git a/services/core/xsd/vts/AndroidTest.xml b/services/core/xsd/vts/AndroidTest.xml
deleted file mode 100644
index 4f3b2ef..0000000
--- a/services/core/xsd/vts/AndroidTest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<configuration description="Config for VTS VtsValidateDefaultPermissions.">
-    <option name="config-descriptor:metadata" key="plan" value="vts-treble" />
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.VtsFilePusher">
-        <option name="abort-on-push-failure" value="false"/>
-        <option name="push-group" value="HostDrivenTest.push"/>
-        <option name="push" value="DATA/etc/default-permissions.xsd->/data/local/tmp/default-permissions.xsd"/>
-    </target_preparer>
-    <test class="com.android.tradefed.testtype.VtsMultiDeviceTest">
-        <option name="test-module-name" value="VtsValidateDefaultPermissions"/>
-        <option name="binary-test-source" value="_32bit::DATA/nativetest/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
-        <option name="binary-test-source" value="_64bit::DATA/nativetest64/vts_defaultPermissions_validate_test/vts_defaultPermissions_validate_test" />
-        <option name="binary-test-type" value="gtest"/>
-        <option name="test-timeout" value="30s"/>
-    </test>
-</configuration>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 09ba6ef..5f6ac10 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2128,10 +2128,11 @@
 
     ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle, boolean parent) {
         ensureLocked();
-        Preconditions.checkCallAuthorization(!parent || isManagedProfile(userHandle),
-                String.format("You can not call APIs on the parent profile outside a "
-                        + "managed profile, userId = %d", userHandle));
-
+        if (parent) {
+            Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
+                    "You can not call APIs on the parent profile outside a managed profile, "
+                            + "userId = %d", userHandle));
+        }
         ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
         if (admin != null && parent) {
             admin = admin.getParentActiveAdmin();
@@ -2301,9 +2302,9 @@
             boolean parent,
             @Nullable String permission) throws SecurityException {
         ensureLocked();
-        Preconditions.checkCallingUser(!parent
-                || isManagedProfile(getCallerIdentity().getUserId()));
-
+        if (parent) {
+            Preconditions.checkCallingUser(isManagedProfile(getCallerIdentity().getUserId()));
+        }
         ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
                 who, reqPolicy, permission);
         return parent ? admin.getParentActiveAdmin() : admin;
@@ -3178,10 +3179,7 @@
             return;
         }
         Objects.requireNonNull(adminReceiver, "ComponentName is null");
-
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call forceRemoveActiveAdmin");
-
+        enforceShell("forceRemoveActiveAdmin");
         mInjector.binderWithCleanCallingIdentity(() -> {
             synchronized (getLockObject()) {
                 if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
@@ -3260,6 +3258,13 @@
         return (admin != null) && admin.testOnlyAdmin;
     }
 
+    private void enforceShell(String method) {
+        final int callingUid = mInjector.binderGetCallingUid();
+        if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
+            throw new SecurityException("Non-shell user attempted to call " + method);
+        }
+    }
+
     @Override
     public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) {
         if (!mHasFeature) {
@@ -3269,8 +3274,7 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkState(mUserManager.isUserUnlocked(userHandle),
-                "User must be running and unlocked");
+        enforceUserUnlocked(userHandle);
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
@@ -3284,8 +3288,10 @@
                         adminReceiver);
                 return;
             }
-            Preconditions.checkCallAuthorization((admin.getUid() == caller.getUid())
-                    || hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+            if (admin.getUid() != mInjector.binderGetCallingUid()) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
+            }
             mInjector.binderWithCleanCallingIdentity(() ->
                     removeActiveAdminLocked(adminReceiver, userHandle));
         }
@@ -3293,8 +3299,7 @@
 
     @Override
     public boolean isSeparateProfileChallengeAllowed(int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query separate challenge support");
+        enforceSystemCaller("query separate challenge support");
 
         ComponentName profileOwner = getProfileOwner(userHandle);
         // Profile challenge is supported on N or newer release.
@@ -4066,9 +4071,7 @@
 
         final CallerIdentity caller = getCallerIdentity();
         Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
-        Preconditions.checkState(
-                mUserManager.isUserUnlocked(parent ? getProfileParentId(userHandle) : userHandle),
-                "User must be running and unlocked");
+        enforceUserUnlocked(userHandle, parent);
 
         synchronized (getLockObject()) {
             // This API can only be called by an active device admin,
@@ -4109,15 +4112,15 @@
         Preconditions.checkCallAuthorization(isManagedProfile(userHandle), String.format(
                 "can not call APIs refering to the parent profile outside a managed profile, "
                         + "userId = %d", userHandle));
-        Preconditions.checkState(mUserManager.isUserUnlocked(getProfileParentId(userHandle)),
-                "User must be running and unlocked");
 
         synchronized (getLockObject()) {
+            final int targetUser = getProfileParentId(userHandle);
+            enforceUserUnlocked(targetUser, false);
             int credentialOwner = getCredentialOwner(userHandle, false);
             DevicePolicyData policy = getUserDataUnchecked(credentialOwner);
             PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
-            return isActivePasswordSufficientForUserLocked(policy.mPasswordValidAtLastCheckpoint,
-                    metrics, getProfileParentId(userHandle), false);
+            return isActivePasswordSufficientForUserLocked(
+                    policy.mPasswordValidAtLastCheckpoint, metrics, targetUser, false);
         }
     }
 
@@ -4133,8 +4136,7 @@
         Preconditions.checkCallAuthorization(!isManagedProfile(userHandle), String.format(
                 "You can not check password sufficiency for a managed profile, userId = %d",
                 userHandle));
-        Preconditions.checkState(mUserManager.isUserUnlocked(userHandle),
-                "User must be running and unlocked");
+        enforceUserUnlocked(userHandle);
 
         synchronized (getLockObject()) {
             PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(userHandle);
@@ -4192,22 +4194,24 @@
     @Override
     @PasswordComplexity
     public int getPasswordComplexity(boolean parent) {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkState(mUserManager.isUserUnlocked(caller.getUserId()),
-                "User must be running and unlocked");
-        Preconditions.checkCallAuthorization(!parent || isCallerDeviceOwner(caller.getUid())
-                || isCallerProfileOwner(caller.getUid()) || isSystemUid(caller));
-        Preconditions.checkCallAuthorization(
-                hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY));
-
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.GET_USER_PASSWORD_COMPLEXITY_LEVEL)
                 .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT,
-                        mInjector.getPackageManager().getPackagesForUid(caller.getUid()))
+                        mInjector.getPackageManager().getPackagesForUid(
+                                mInjector.binderGetCallingUid()))
                 .write();
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+
+        if (parent) {
+            enforceProfileOwnerOrSystemUser();
+        }
+        enforceUserUnlocked(callingUserId);
+        mContext.enforceCallingOrSelfPermission(
+                REQUEST_PASSWORD_COMPLEXITY,
+                "Must have " + REQUEST_PASSWORD_COMPLEXITY + " permission.");
 
         synchronized (getLockObject()) {
-            final int credentialOwner = getCredentialOwner(caller.getUserId(), parent);
+            final int credentialOwner = getCredentialOwner(callingUserId, parent);
             PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
             return metrics == null ? PASSWORD_COMPLEXITY_NONE : metrics.determineComplexity();
         }
@@ -4356,24 +4360,22 @@
             Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
             return false;
         }
+        if (password == null) password = "";
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userHandle = mInjector.userHandleGetCallingUserId();
 
-        final CallerIdentity caller = getCallerIdentity();
-        // As of R, only privileged caller holding RESET_PASSWORD can call resetPassword() to
+        // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to
         // set password to an unsecured user.
         if (hasCallingPermission(permission.RESET_PASSWORD)) {
-            if (password == null) {
-                password = "";
-            }
-            return setPasswordPrivileged(password, flags, caller.getUid());
+            return setPasswordPrivileged(password, flags, callingUid);
         }
 
         synchronized (getLockObject()) {
             // If caller has PO (or DO) throw or fail silently depending on its target SDK level.
             ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked(
-                    null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, caller.getUid());
+                    null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid);
             if (admin != null) {
-                if (getTargetSdk(admin.info.getPackageName(),
-                        caller.getUserId()) < Build.VERSION_CODES.O) {
+                if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
                     Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
                     return false;
                 }
@@ -4384,7 +4386,7 @@
             admin = getActiveAdminForCallerLocked(
                     null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD, false);
             if (getTargetSdk(admin.info.getPackageName(),
-                    caller.getUserId()) <= android.os.Build.VERSION_CODES.M) {
+                    userHandle) <= android.os.Build.VERSION_CODES.M) {
                 Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
                 return false;
             }
@@ -4480,9 +4482,8 @@
 
     @Override
     public boolean getDoNotAskCredentialsOnBoot() {
-        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
-                permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT));
-
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT, null);
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
             return policyData.mDoNotAskCredentialsOnBoot;
@@ -5224,13 +5225,12 @@
     @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isSystemUid(caller),
-                "Only the system can choose private key alias");
+        enforceSystemCaller("choose private key alias");
 
+        final UserHandle caller = mInjector.binderGetCallingUserHandle();
         // If there is a profile owner, redirect to that; otherwise query the device owner.
-        ComponentName aliasChooser = getProfileOwner(caller.getUserId());
-        if (aliasChooser == null && caller.getUserHandle().isSystem()) {
+        ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
+        if (aliasChooser == null && caller.isSystem()) {
             synchronized (getLockObject()) {
                 final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
                 if (deviceOwnerAdmin != null) {
@@ -5252,7 +5252,7 @@
 
         final ComponentName delegateReceiver;
         delegateReceiver = resolveDelegateReceiver(DELEGATION_CERT_SELECTION,
-                DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getUserId());
+                DeviceAdminReceiver.ACTION_CHOOSE_PRIVATE_KEY_ALIAS, caller.getIdentifier());
 
         final boolean isDelegate;
         if (delegateReceiver != null) {
@@ -5264,8 +5264,7 @@
         }
 
         mInjector.binderWithCleanCallingIdentity(() -> {
-            mContext.sendOrderedBroadcastAsUser(intent, caller.getUserHandle(), null,
-                    new BroadcastReceiver() {
+            mContext.sendOrderedBroadcastAsUser(intent, caller, null, new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
                     final String chosenAlias = getResultData();
@@ -5429,14 +5428,21 @@
             String delegatePackage) throws SecurityException {
         Objects.requireNonNull(delegatePackage, "Delegate package is null");
 
-        final CallerIdentity caller = getCallerIdentity(who, delegatePackage);
-        Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isDeviceOwner(caller)
-                || isProfileOwner(caller))) || (caller.hasPackage() && isCallingFromPackage(
-                delegatePackage, caller.getUid())));
-
         // Retrieve the user ID of the calling process.
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
         synchronized (getLockObject()) {
-            final DevicePolicyData policy = getUserData(caller.getUserId());
+            // Ensure calling process is device/profile owner.
+            if (who != null) {
+                getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            // Or ensure calling process is delegatePackage itself.
+            } else {
+                if (!isCallingFromPackage(delegatePackage, callingUid)) {
+                    throw new SecurityException("Caller with uid " + callingUid + " is not "
+                            + delegatePackage);
+                }
+            }
+            final DevicePolicyData policy = getUserData(userId);
             // Retrieve the scopes assigned to delegatePackage, or null if no scope was given.
             final List<String> scopes = policy.mDelegationMap.get(delegatePackage);
             return scopes == null ? Collections.EMPTY_LIST : scopes;
@@ -5715,9 +5721,7 @@
 
     @Override
     public String getAlwaysOnVpnPackageForUser(int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can get always on VPN package for user");
-
+        enforceSystemCaller("getAlwaysOnVpnPackageForUser");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
             return admin != null ? admin.mAlwaysOnVpnPackage : null;
@@ -5738,9 +5742,7 @@
 
     @Override
     public boolean isAlwaysOnVpnLockdownEnabledForUser(int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query always on VPN lockdown enabled for user");
-
+        enforceSystemCaller("isAlwaysOnVpnLockdownEnabledForUser");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
             return admin != null ? admin.mAlwaysOnVpnLockdown : null;
@@ -5971,18 +5973,19 @@
         if (!mHasFeature) {
             return null;
         }
-        final CallerIdentity caller = getCallerIdentityOptionalAdmin(who);
 
         final int frpManagementAgentUid = getFrpManagementAgentUidOrThrow();
         final ActiveAdmin admin;
         synchronized (getLockObject()) {
             if (who == null) {
-                Preconditions.checkCallAuthorization(frpManagementAgentUid == caller.getUid()
+                Preconditions.checkCallAuthorization(
+                        frpManagementAgentUid == mInjector.binderGetCallingUid()
                                 || hasCallingPermission(permission.MASTER_CLEAR),
                         "Must be called by the FRP management agent on device");
                 admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
                         UserHandle.getUserId(frpManagementAgentUid));
             } else {
+                final CallerIdentity caller = getCallerIdentity(who);
                 Preconditions.checkCallAuthorization(
                         isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller));
                 admin = getProfileOwnerOrDeviceOwnerLocked(caller);
@@ -6046,7 +6049,9 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isSystemUid(caller));
         // Managed Profile password can only be changed when it has a separate challenge.
         if (!isSeparateProfileChallengeEnabled(userId)) {
             Preconditions.checkCallAuthorization(!isManagedProfile(userId), String.format("You can "
@@ -6566,8 +6571,9 @@
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(!parent
-                || isProfileOwnerOfOrganizationOwnedDevice(caller));
+        if (parent) {
+            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+        }
 
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
@@ -6941,8 +6947,9 @@
         Objects.requireNonNull(who, "ComponentName is null");
 
         final CallerIdentity caller = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(!parent
-                || isProfileOwnerOfOrganizationOwnedDevice(caller));
+        if (parent) {
+            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
+        }
 
         final int userHandle = caller.getUserId();
         synchronized (getLockObject()) {
@@ -7180,14 +7187,15 @@
                     + " as device owner for user " + userId);
             return false;
         }
-        Objects.requireNonNull(admin, "ComponentName is null");
-        Preconditions.checkArgument(isPackageInstalledForUser(admin.getPackageName(), userId),
-                String.format("Invalid component %s for device owner", admin));
-
-        final CallerIdentity caller = getCallerIdentity();
+        if (admin == null
+                || !isPackageInstalledForUser(admin.getPackageName(), userId)) {
+            throw new IllegalArgumentException("Invalid component " + admin
+                    + " for device owner");
+        }
+        final boolean hasIncompatibleAccountsOrNonAdb =
+                hasIncompatibleAccountsOrNonAdbNoLock(userId, admin);
         synchronized (getLockObject()) {
-            enforceCanSetDeviceOwnerLocked(caller, userId);
-
+            enforceCanSetDeviceOwnerLocked(admin, userId, hasIncompatibleAccountsOrNonAdb);
             final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
             if (activeAdmin == null
                     || getUserData(userId).mRemovingAdmins.contains(admin)) {
@@ -7196,7 +7204,7 @@
 
             // Shutting down backup manager service permanently.
             toggleBackupServiceActive(UserHandle.USER_SYSTEM, /* makeActive= */ false);
-            if (isAdb(caller)) {
+            if (isAdb()) {
                 // Log device owner provisioning was started using adb.
                 MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_DEVICE_OWNER);
                 DevicePolicyEventLogger
@@ -7234,13 +7242,14 @@
 
     @Override
     public boolean hasDeviceOwner() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller));
-
+        enforceDeviceOwnerOrManageUsers();
         return mOwners.hasDeviceOwner();
     }
 
+    boolean isDeviceOwner(ActiveAdmin admin) {
+        return isDeviceOwner(admin.info.getComponent(), admin.getUserHandle().getIdentifier());
+    }
+
     public boolean isDeviceOwner(ComponentName who, int userId) {
         synchronized (getLockObject()) {
             return mOwners.hasDeviceOwner()
@@ -7406,20 +7415,20 @@
     @Override
     public void clearDeviceOwner(String packageName) {
         Objects.requireNonNull(packageName, "packageName is null");
-
-        final CallerIdentity caller = getCallerIdentity(packageName);
-        Preconditions.checkCallAuthorization(isCallingFromPackage(packageName, caller.getUid()),
-                "Invalid packageName");
-
+        final int callingUid = mInjector.binderGetCallingUid();
+        if (!isCallingFromPackage(packageName, callingUid)) {
+            throw new SecurityException("Invalid packageName");
+        }
         synchronized (getLockObject()) {
             final ComponentName deviceOwnerComponent = mOwners.getDeviceOwnerComponent();
-            final int deviceOwnerUserId = caller.getUserId();
-            Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
-                    && deviceOwnerComponent.getPackageName().equals(packageName),
-                    "clearDeviceOwner can only be called by the device owner");
-            Preconditions.checkState(mUserManager.isUserUnlocked(deviceOwnerUserId),
-                    "User must be running and unlocked");
-
+            final int deviceOwnerUserId = mOwners.getDeviceOwnerUserId();
+            if (!mOwners.hasDeviceOwner()
+                    || !deviceOwnerComponent.getPackageName().equals(packageName)
+                    || (deviceOwnerUserId != UserHandle.getUserId(callingUid))) {
+                throw new SecurityException(
+                        "clearDeviceOwner can only be called by the device owner");
+            }
+            enforceUserUnlocked(deviceOwnerUserId);
             DevicePolicyData policy = getUserData(deviceOwnerUserId);
             if (policy.mPasswordTokenHandle != 0) {
                 mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, deviceOwnerUserId);
@@ -7504,13 +7513,16 @@
                     + " as profile owner for user " + userHandle);
             return false;
         }
-        Objects.requireNonNull(who, "ComponentName is null");
-        Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
-                String.format("Component %s not installed for userId: %d", who, userHandle));
+        if (who == null
+                || !isPackageInstalledForUser(who.getPackageName(), userHandle)) {
+            throw new IllegalArgumentException("Component " + who
+                    + " not installed for userId:" + userHandle);
+        }
 
-        final CallerIdentity caller = getCallerIdentity();
+        final boolean hasIncompatibleAccountsOrNonAdb =
+                hasIncompatibleAccountsOrNonAdbNoLock(userHandle, who);
         synchronized (getLockObject()) {
-            enforceCanSetProfileOwnerLocked(caller, userHandle);
+            enforceCanSetProfileOwnerLocked(who, userHandle, hasIncompatibleAccountsOrNonAdb);
 
             final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
             if (admin == null || getUserData(userHandle).mRemovingAdmins.contains(who)) {
@@ -7528,7 +7540,7 @@
                 return false;
             }
 
-            if (isAdb(caller)) {
+            if (isAdb()) {
                 // Log profile owner provisioning was started using adb.
                 MetricsLogger.action(mContext, PROVISIONING_ENTRY_POINT_ADB, LOG_TAG_PROFILE_OWNER);
                 DevicePolicyEventLogger
@@ -7561,47 +7573,6 @@
         }
     }
 
-    /**
-     * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
-     * permission.
-     * The profile owner can only be set before the user setup phase has completed,
-     * except for:
-     * - SYSTEM_UID
-     * - adb unless hasIncompatibleAccountsOrNonAdb is true.
-     */
-    private void enforceCanSetProfileOwnerLocked(CallerIdentity caller, int userHandle) {
-        UserInfo info = getUserInfo(userHandle);
-        Preconditions.checkArgument(info != null,
-                String.format("Attempted to set profile owner for invalid userId: %d", userHandle));
-        Preconditions.checkState(!info.isGuest(), "Cannot set a profile owner on a guest");
-        Preconditions.checkState(!mOwners.hasProfileOwner(userHandle),
-                "Trying to set the profile owner, but profile owner is already set.");
-        Preconditions.checkState(
-                !mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != userHandle,
-                "Trying to set the profile owner, but the user already has a device owner.");
-
-
-        boolean hasUserSetupCompleted = mIsWatch || hasUserSetupCompleted(userHandle);
-        if (isAdb(caller)) {
-            Preconditions.checkState(!hasUserSetupCompleted
-                            || !hasIncompatibleAccountsOrNonAdbNoLock(userHandle, caller),
-                    "Not allowed to set the profile owner because there are already some accounts"
-                            + " on the profile");
-            return;
-        }
-        Preconditions.checkCallAuthorization(
-                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-        if (hasUserSetupCompleted) {
-            Preconditions.checkState(isSystemUid(caller),
-                    "Cannot set the profile owner on a user which is already set-up");
-            if (!mIsWatch) {
-                Preconditions.checkState(isDefaultSupervisor(caller),
-                        String.format("Unable to set non-default profile owner post-setup %s",
-                                caller.getUserHandle()));
-            }
-        }
-    }
-
     private void toggleBackupServiceActive(int userId, boolean makeActive) {
         long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -7628,9 +7599,8 @@
         final CallerIdentity caller = getCallerIdentity(who);
         final int userId = caller.getUserId();
         Preconditions.checkCallingUser(!isManagedProfile(userId));
-        Preconditions.checkState(mUserManager.isUserUnlocked(userId),
-                "User must be running and unlocked");
 
+        enforceUserUnlocked(userId);
         synchronized (getLockObject()) {
             // Check if this is the profile owner who is calling
             final ActiveAdmin admin =
@@ -7763,24 +7733,28 @@
                     + userHandle);
             return;
         }
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkState(userHandle == mOwners.getDeviceOwnerUserId()
-                        || hasProfileOwner(userHandle) || isManagedProfile(caller.getUserId()),
-                "Not allowed to change provisioning state unless "
-                        + "a device or profile owner is set.");
+
+        if (userHandle != mOwners.getDeviceOwnerUserId() && !mOwners.hasProfileOwner(userHandle)
+                && getManagedUserId(userHandle) == -1) {
+            // No managed device, user or profile, so setting provisioning state makes no sense.
+            throw new IllegalStateException("Not allowed to change provisioning state unless a "
+                      + "device or profile owner is set.");
+        }
 
         synchronized (getLockObject()) {
             boolean transitionCheckNeeded = true;
 
             // Calling identity/permission checks.
-            if (isAdb(caller)) {
+            if (isAdb()) {
                 // ADB shell can only move directly from un-managed to finalized as part of directly
                 // setting profile-owner or device-owner.
-                Preconditions.checkState(getUserProvisioningState(userHandle)
-                                == DevicePolicyManager.STATE_USER_UNMANAGED
-                                && newState == DevicePolicyManager.STATE_USER_SETUP_FINALIZED,
-                        "Not allowed to change provisioning state unless current provisioning "
-                                + "state is unmanaged, and new state is finalized.");
+                if (getUserProvisioningState(userHandle) !=
+                        DevicePolicyManager.STATE_USER_UNMANAGED
+                        || newState != DevicePolicyManager.STATE_USER_SETUP_FINALIZED) {
+                    throw new IllegalStateException("Not allowed to change provisioning state "
+                            + "unless current provisioning state is unmanaged, and new state is "
+                            + "finalized.");
+                }
                 transitionCheckNeeded = false;
             } else {
                 Preconditions.checkCallAuthorization(
@@ -8016,7 +7990,7 @@
 
     @Override
     public boolean checkDeviceIdentifierAccess(String packageName, int pid, int uid) {
-        enforceCallerIdentityMatchesIfNotSystem(packageName, pid, uid);
+        ensureCallerIdentityMatchesIfNotSystem(packageName, pid, uid);
 
         // Verify that the specified packages matches the provided uid.
         if (!doesPackageMatchUid(packageName, uid)) {
@@ -8080,16 +8054,16 @@
         return true;
     }
 
-    private void enforceCallerIdentityMatchesIfNotSystem(String packageName, int pid, int uid) {
+    private void ensureCallerIdentityMatchesIfNotSystem(String packageName, int pid, int uid) {
         // If the caller is not a system app then it should only be able to check its own device
         // identifier access.
+        int callingUid = mInjector.binderGetCallingUid();
         int callingPid = mInjector.binderGetCallingPid();
-        final CallerIdentity caller = getCallerIdentity();
-        if (UserHandle.getAppId(caller.getUid()) >= Process.FIRST_APPLICATION_UID
-                && (caller.getUid() != uid || callingPid != pid)) {
-            String message = String.format("Calling uid %d, pid %d cannot check device identifier "
-                    + "access for package %s (uid=%d, pid=%d)",
-                    caller.getUid(), callingPid, packageName, uid, pid);
+        if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID
+                && (callingUid != uid || callingPid != pid)) {
+            String message = String.format(
+                    "Calling uid %d, pid %d cannot check device identifier access for package %s "
+                            + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid);
             Log.w(LOG_TAG, message);
             throw new SecurityException(message);
         }
@@ -8127,18 +8101,83 @@
     }
 
     /**
+     * The profile owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
+     * permission.
+     * The profile owner can only be set before the user setup phase has completed,
+     * except for:
+     * - SYSTEM_UID
+     * - adb unless hasIncompatibleAccountsOrNonAdb is true.
+     */
+    private void enforceCanSetProfileOwnerLocked(@Nullable ComponentName owner, int userHandle,
+            boolean hasIncompatibleAccountsOrNonAdb) {
+        UserInfo info = getUserInfo(userHandle);
+        if (info == null) {
+            // User doesn't exist.
+            throw new IllegalArgumentException(
+                    "Attempted to set profile owner for invalid userId: " + userHandle);
+        }
+        if (info.isGuest()) {
+            throw new IllegalStateException("Cannot set a profile owner on a guest");
+        }
+        if (mOwners.hasProfileOwner(userHandle)) {
+            throw new IllegalStateException("Trying to set the profile owner, but profile owner "
+                    + "is already set.");
+        }
+        if (mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerUserId() == userHandle) {
+            throw new IllegalStateException("Trying to set the profile owner, but the user "
+                    + "already has a device owner.");
+        }
+        if (isAdb()) {
+            if ((mIsWatch || hasUserSetupCompleted(userHandle))
+                    && hasIncompatibleAccountsOrNonAdb) {
+                throw new IllegalStateException("Not allowed to set the profile owner because "
+                        + "there are already some accounts on the profile");
+            }
+            return;
+        }
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        if ((mIsWatch || hasUserSetupCompleted(userHandle))) {
+            if (!isCallerWithSystemUid()) {
+                throw new IllegalStateException("Cannot set the profile owner on a user which is "
+                        + "already set-up");
+            }
+
+            if (!mIsWatch) {
+                // Only the default supervision profile owner can be set as profile owner after SUW
+                final String supervisor = mContext.getResources().getString(
+                        com.android.internal.R.string
+                                .config_defaultSupervisionProfileOwnerComponent);
+                if (supervisor == null) {
+                    throw new IllegalStateException("Unable to set profile owner post-setup, no"
+                            + "default supervisor profile owner defined");
+                }
+
+                final ComponentName supervisorComponent = ComponentName.unflattenFromString(
+                        supervisor);
+                if (!owner.equals(supervisorComponent)) {
+                    throw new IllegalStateException("Unable to set non-default profile owner"
+                            + " post-setup " + owner);
+                }
+            }
+        }
+    }
+
+    /**
      * The Device owner can only be set by adb or an app with the MANAGE_PROFILE_AND_DEVICE_OWNERS
      * permission.
      */
-    private void enforceCanSetDeviceOwnerLocked(CallerIdentity caller, int userId) {
-        if (!isAdb(caller)) {
+    private void enforceCanSetDeviceOwnerLocked(@Nullable ComponentName owner,
+            @UserIdInt int userId,
+            boolean hasIncompatibleAccountsOrNonAdb) {
+        if (!isAdb()) {
             Preconditions.checkCallAuthorization(
                     hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
         }
 
         final int code = checkDeviceOwnerProvisioningPreConditionLocked(
-                caller.getComponentName(), userId, isAdb(caller),
-                hasIncompatibleAccountsOrNonAdbNoLock(userId, caller));
+                owner, userId, isAdb(), hasIncompatibleAccountsOrNonAdb);
         if (code != CODE_OK) {
             throw new IllegalStateException(computeProvisioningErrorString(code, userId));
         }
@@ -8173,6 +8212,21 @@
 
     }
 
+    private void enforceUserUnlocked(int userId) {
+        // Since we're doing this operation on behalf of an app, we only
+        // want to use the actual "unlocked" state.
+        Preconditions.checkState(mUserManager.isUserUnlocked(userId),
+                "User must be running and unlocked");
+    }
+
+    private void enforceUserUnlocked(@UserIdInt int userId, boolean parent) {
+        if (parent) {
+            enforceUserUnlocked(getProfileParentId(userId));
+        } else {
+            enforceUserUnlocked(userId);
+        }
+    }
+
     private boolean canManageUsers(CallerIdentity caller) {
         return isSystemUid(caller) || isRootUid(caller)
                 || hasCallingOrSelfPermission(permission.MANAGE_USERS);
@@ -8205,6 +8259,42 @@
                 || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
     }
 
+    private void enforceDeviceOwnerOrManageUsers() {
+        synchronized (getLockObject()) {
+            if (getActiveAdminWithPolicyForUidLocked(null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER,
+                    mInjector.binderGetCallingUid()) != null) {
+                return;
+            }
+        }
+        Preconditions.checkCallAuthorization(canManageUsers(getCallerIdentity()));
+    }
+
+    private void enforceProfileOwnerOrSystemUser() {
+        synchronized (getLockObject()) {
+            if (getActiveAdminWithPolicyForUidLocked(null,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
+                            != null) {
+                return;
+            }
+        }
+        Preconditions.checkState(isCallerWithSystemUid(),
+                "Only profile owner, device owner and system may call this method.");
+    }
+
+    private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity caller,
+            int userId) {
+        if (userId == caller.getUserId()) {
+            synchronized (getLockObject()) {
+                if (getActiveAdminWithPolicyForUidLocked(null,
+                        DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, caller.getUid()) != null) {
+                    // Device Owner/Profile Owner may access the user it runs on.
+                    return;
+                }
+            }
+        }
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+    }
+
     private boolean canUserUseLockTaskLocked(int userId) {
         if (isUserAffiliatedWithDeviceLocked(userId)) {
             return true;
@@ -8228,22 +8318,34 @@
         return true;
     }
 
+    private void enforceCanCallLockTaskLocked(ComponentName who) {
+        getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+        final int userId =  mInjector.userHandleGetCallingUserId();
+        if (!canUserUseLockTaskLocked(userId)) {
+            throw new SecurityException("User " + userId + " is not allowed to use lock task");
+        }
+    }
+
     private void ensureCallerPackage(@Nullable String packageName) {
-        final CallerIdentity caller = getCallerIdentity();
         if (packageName == null) {
-            Preconditions.checkCallAuthorization(isSystemUid(caller),
-                    "Only the system can omit package name");
+            enforceSystemCaller("omit package name");
         } else {
+            final int callingUid = mInjector.binderGetCallingUid();
+            final int userId = mInjector.userHandleGetCallingUserId();
             try {
                 final ApplicationInfo ai = mIPackageManager.getApplicationInfo(
-                        packageName, 0, caller.getUserId());
-                Preconditions.checkState(ai.uid == caller.getUid(), "Unmatching package name");
+                        packageName, 0, userId);
+                Preconditions.checkState(ai.uid == callingUid, "Unmatching package name");
             } catch (RemoteException e) {
                 // Shouldn't happen
             }
         }
     }
 
+    private boolean isCallerWithSystemUid() {
+        return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
+    }
+
     private boolean isSystemUid(CallerIdentity caller) {
         return UserHandle.isSameApp(caller.getUid(), Process.SYSTEM_UID);
     }
@@ -8431,9 +8533,10 @@
         final CallerIdentity caller = getCallerIdentity(admin);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller)
                 || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
-        mInjector.binderWithCleanCallingIdentity(() -> Preconditions.checkArgument(!parent
-                        || isSystemPackage(packageName, getProfileParentId(caller.getUserId())),
-                "The provided package is not a system package"));
+        if (parent) {
+            mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage(
+                    packageName, getProfileParentId(mInjector.userHandleGetCallingUserId())));
+        }
 
         mInjector.binderWithCleanCallingIdentity(() ->
                 SmsApplication.setDefaultApplication(packageName, mContext));
@@ -8459,7 +8562,7 @@
 
     @Override
     public boolean isCallerApplicationRestrictionsManagingPackage(String callerPackage) {
-        return isCallerDelegate(callerPackage, getCallerIdentity().getUid(),
+        return isCallerDelegate(callerPackage, mInjector.binderGetCallingUid(),
                 DELEGATION_APP_RESTRICTIONS);
     }
 
@@ -8574,9 +8677,7 @@
 
     @Override
     public ComponentName getRestrictionsProvider(int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query the permission provider");
-
+        enforceSystemCaller("query the permission provider");
         synchronized (getLockObject()) {
             DevicePolicyData userData = getUserData(userHandle);
             return userData != null ? userData.mRestrictionsProvider : null;
@@ -8844,9 +8945,7 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
-
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query if an accessibility service is disabled by admin");
+        enforceSystemCaller("query if an accessibility service is disabled by admin");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -8966,9 +9065,7 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
-
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query if an input method is disabled by admin");
+        enforceSystemCaller("query if an input method is disabled by admin");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -9025,10 +9122,9 @@
         if (!mHasFeature) {
             return true;
         }
-        Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty");
 
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query if a notification listener service is permitted");
+        Preconditions.checkStringNotEmpty(packageName, "packageName is null or empty");
+        enforceSystemCaller("query if a notification listener service is permitted");
 
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
@@ -9041,6 +9137,12 @@
         }
     }
 
+    private void enforceSystemCaller(String action) {
+        if (!isCallerWithSystemUid()) {
+            throw new SecurityException("Only the system can " + action);
+        }
+    }
+
     private void maybeSendAdminEnabledBroadcastLocked(int userHandle) {
         DevicePolicyData policyData = getUserData(userHandle);
         if (policyData.mAdminBroadcastPending) {
@@ -9070,14 +9172,14 @@
             ComponentName profileOwner, PersistableBundle adminExtras, int flags) {
         Objects.requireNonNull(admin, "admin is null");
         Objects.requireNonNull(profileOwner, "profileOwner is null");
-        Preconditions.checkArgument(admin.getPackageName().equals(profileOwner.getPackageName()),
-                String.format("profileOwner %s and admin %s are not in the same package",
-                        profileOwner, admin));
-
-        final CallerIdentity caller = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
-                "createAndManageUser was called from non-system user");
-
+        if (!admin.getPackageName().equals(profileOwner.getPackageName())) {
+            throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
+                    + admin + " are not in the same package");
+        }
+        // Only allow the system user to use this method
+        if (!mInjector.binderGetCallingUserHandle().isSystem()) {
+            throw new SecurityException("createAndManageUser was called from non-system user");
+        }
         final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0;
         final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0
                 && UserManager.isDeviceInDemoMode(mContext);
@@ -9087,12 +9189,13 @@
         // Create user.
         UserHandle user = null;
         synchronized (getLockObject()) {
-            Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
+            final int callingUid = mInjector.binderGetCallingUid();
             final long id = mInjector.binderClearCallingIdentity();
             try {
-                targetSdkVersion = mInjector.getPackageManagerInternal()
-                        .getUidTargetSdkVersion(caller.getUid());
+                targetSdkVersion = mInjector.getPackageManagerInternal().getUidTargetSdkVersion(
+                        callingUid);
 
                 // Return detail error code for checks inside
                 // UserManagerService.createUserInternalUnchecked.
@@ -9620,8 +9723,7 @@
                 // API cannot be used to leak if certain non-system package exists in the person
                 // profile.
                 mInjector.binderWithCleanCallingIdentity(() ->
-                        Preconditions.checkArgument(isSystemPackage(packageName, userId),
-                        "The provided package is not a system package"));
+                        enforcePackageIsSystemPackage(packageName, userId));
             }
             result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
                     .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
@@ -9652,8 +9754,7 @@
                         && isManagedProfile(caller.getUserId()));
                 // Ensure the package provided is a system package.
                 mInjector.binderWithCleanCallingIdentity(() ->
-                        Preconditions.checkArgument(isSystemPackage(packageName, userId),
-                                "The provided package is not a system package"));
+                        enforcePackageIsSystemPackage(packageName, userId));
             }
 
             return mInjector.binderWithCleanCallingIdentity(
@@ -9661,12 +9762,16 @@
         }
     }
 
-    private boolean isSystemPackage(String packageName, int userId)
+    private void enforcePackageIsSystemPackage(String packageName, int userId)
             throws RemoteException {
+        boolean isSystem;
         try {
-            return isSystemApp(mIPackageManager, packageName, userId);
+            isSystem = isSystemApp(mIPackageManager, packageName, userId);
         } catch (IllegalArgumentException e) {
-            return false;
+            isSystem = false;
+        }
+        if (!isSystem) {
+            throw new IllegalArgumentException("The provided package is not a system package");
         }
     }
 
@@ -10215,12 +10320,10 @@
         Objects.requireNonNull(who, "ComponentName is null");
         Objects.requireNonNull(packages, "packages is null");
 
-        final CallerIdentity caller = getCallerIdentity(who);
         synchronized (getLockObject()) {
-            Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller))
-                            && canUserUseLockTaskLocked(caller.getUserId()),
-                    String.format("User %d is not allowed to use lock task", caller.getUserId()));
-            setLockTaskPackagesLocked(caller.getUserId(), new ArrayList<>(Arrays.asList(packages)));
+            enforceCanCallLockTaskLocked(who);
+            final int userHandle = mInjector.userHandleGetCallingUserId();
+            setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
         }
     }
 
@@ -10237,12 +10340,10 @@
     public String[] getLockTaskPackages(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
 
-        final CallerIdentity caller = getCallerIdentity(who);
+        final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
         synchronized (getLockObject()) {
-            Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller))
-                            && canUserUseLockTaskLocked(caller.getUserId()),
-                    String.format("User %d is not allowed to use lock task", caller.getUserId()));
-            final List<String> packages = getUserData(caller.getUserId()).mLockTaskPackages;
+            enforceCanCallLockTaskLocked(who);
+            final List<String> packages = getUserData(userHandle).mLockTaskPackages;
             return packages.toArray(new String[packages.size()]);
         }
     }
@@ -10258,6 +10359,7 @@
     @Override
     public void setLockTaskFeatures(ComponentName who, int flags) {
         Objects.requireNonNull(who, "ComponentName is null");
+
         // Throw if Overview is used without Home.
         boolean hasHome = (flags & LOCK_TASK_FEATURE_HOME) != 0;
         boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0;
@@ -10267,12 +10369,10 @@
         Preconditions.checkArgument(hasHome || !hasNotification,
             "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
 
-        final CallerIdentity caller = getCallerIdentity(who);
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller))
-                            && canUserUseLockTaskLocked(caller.getUserId()),
-                    String.format("User %d is not allowed to use lock task", caller.getUserId()));
-            setLockTaskFeaturesLocked(caller.getUserId(), flags);
+            enforceCanCallLockTaskLocked(who);
+            setLockTaskFeaturesLocked(userHandle, flags);
         }
     }
 
@@ -10286,13 +10386,10 @@
     @Override
     public int getLockTaskFeatures(ComponentName who) {
         Objects.requireNonNull(who, "ComponentName is null");
-
-        final CallerIdentity caller = getCallerIdentity(who);
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            Preconditions.checkCallAuthorization((isDeviceOwner(caller) || isProfileOwner(caller))
-                            && canUserUseLockTaskLocked(caller.getUserId()),
-                    String.format("User %d is not allowed to use lock task", caller.getUserId()));
-            return getUserData(caller.getUserId()).mLockTaskFeatures;
+            enforceCanCallLockTaskLocked(who);
+            return getUserData(userHandle).mLockTaskFeatures;
         }
     }
 
@@ -10323,9 +10420,7 @@
 
     @Override
     public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can notify lock task mode changed");
-
+        enforceSystemCaller("call notifyLockTaskModeChanged");
         synchronized (getLockObject()) {
             final DevicePolicyData policy = getUserData(userHandle);
 
@@ -10950,9 +11045,18 @@
         }
 
         @Override
-        public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) {
+        public boolean isActiveDeviceOwner(int uid) {
             synchronized (getLockObject()) {
-                return getActiveAdminWithPolicyForUidLocked(null, reqPolicy, uid) != null;
+                return getActiveAdminWithPolicyForUidLocked(
+                        null, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER, uid) != null;
+            }
+        }
+
+        @Override
+        public boolean isActiveProfileOwner(int uid) {
+            synchronized (getLockObject()) {
+                return getActiveAdminWithPolicyForUidLocked(
+                        null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, uid) != null;
             }
         }
 
@@ -11076,8 +11180,8 @@
                 return false;
             }
             if (isUserAffiliatedWithDevice(UserHandle.getUserId(callerUid))
-                    && isActiveAdminWithPolicy(callerUid,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)) {
+                    && (isActiveProfileOwner(callerUid)
+                        || isActiveDeviceOwner(callerUid))) {
                 // device owner or a profile owner affiliated with the device owner
                 return true;
             }
@@ -11299,13 +11403,13 @@
     @Override
     public Intent createAdminSupportIntent(String restriction) {
         Objects.requireNonNull(restriction);
-
-        final CallerIdentity caller = getCallerIdentity();
+        final int uid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(uid);
         Intent intent = null;
         if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
                 DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
             synchronized (getLockObject()) {
-                final DevicePolicyData policy = getUserData(caller.getUserId());
+                final DevicePolicyData policy = getUserData(userId);
                 final int N = policy.mAdminList.size();
                 for (int i = 0; i < N; i++) {
                     final ActiveAdmin admin = policy.mAdminList.get(i);
@@ -11313,8 +11417,7 @@
                                 DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
                         (admin.disableScreenCapture && DevicePolicyManager
                                 .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
-                        intent = createShowAdminSupportIntent(admin.info.getComponent(),
-                                caller.getUserId());
+                        intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
                         break;
                     }
                 }
@@ -11331,8 +11434,7 @@
             }
         } else {
             // if valid, |restriction| can only be a user restriction
-            intent = mLocalService.createUserRestrictionSupportIntent(
-                    caller.getUserId(), restriction);
+            intent = mLocalService.createUserRestrictionSupportIntent(userId, restriction);
         }
         if (intent != null) {
             intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
@@ -11465,9 +11567,7 @@
 
     @Override
     public void clearSystemUpdatePolicyFreezePeriodRecord() {
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call clearSystemUpdatePolicyFreezePeriodRecord");
-
+        enforceShell("clearSystemUpdatePolicyFreezePeriodRecord");
         synchronized (getLockObject()) {
             // Print out current record to help diagnosed CTS failures
             Slog.i(LOG_TAG, "Clear freeze period record: "
@@ -11479,8 +11579,7 @@
     }
 
     /**
-     * Checks if the caller of the method is the device owner app. This method should only be called
-     * if not componentName is available.
+     * Checks if the caller of the method is the device owner app.
      *
      * @param callerUid UID of the caller.
      * @return true if the caller is the device owner app
@@ -11496,47 +11595,26 @@
             }
             final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent()
                     .getPackageName();
-            try {
-                String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid);
-                if (pkgs != null) {
+                try {
+                    String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid);
                     for (String pkg : pkgs) {
                         if (deviceOwnerPackageName.equals(pkg)) {
                             return true;
                         }
                     }
+                } catch (RemoteException e) {
+                    return false;
                 }
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the caller of the method is the profile owner. This method should only be called
-     * if not componentName is available.
-     *
-     * @param callerUid UID of the caller.
-     * @return true if the caller is the profile owner
-     */
-    private boolean isCallerProfileOwner(int callerUid) {
-        final int userId = UserHandle.getUserId(callerUid);
-        for (ActiveAdmin admin : getUserData(userId).mAdminList) {
-            if (admin.getUid() == callerUid && isProfileOwner(admin.info.getComponent(), userId)) {
-                return true;
-            }
         }
         return false;
     }
 
     @Override
     public void notifyPendingSystemUpdate(@Nullable SystemUpdateInfo info) {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE),
+        mContext.enforceCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE,
                 "Only the system update service can broadcast update information");
 
-        if (!caller.getUserHandle().isSystem()) {
+        if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
             Slog.w(LOG_TAG, "Only the system update service in the system user " +
                     "can broadcast update information.");
             return;
@@ -11764,12 +11842,12 @@
     public boolean isProvisioningAllowed(String action, String packageName) {
         Objects.requireNonNull(packageName);
 
-        final CallerIdentity caller = getCallerIdentity();
+        final int callingUid = mInjector.binderGetCallingUid();
         final long ident = mInjector.binderClearCallingIdentity();
         try {
             final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
-                    packageName, caller.getUserId());
-            Preconditions.checkArgument(caller.getUid() == uidForPackage,
+                    packageName, UserHandle.getUserId(callingUid));
+            Preconditions.checkArgument(callingUid == uidForPackage,
                     "Caller uid doesn't match the one for the provided package.");
         } catch (NameNotFoundException e) {
             throw new IllegalArgumentException("Invalid package provided " + packageName, e);
@@ -12080,18 +12158,17 @@
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
-        final CallerIdentity caller = getCallerIdentity();
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who, caller.getUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             if (!TextUtils.equals(admin.shortSupportMessage, message)) {
                 admin.shortSupportMessage = message;
-                saveSettingsLocked(caller.getUserId());
+                saveSettingsLocked(userHandle);
             }
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_SHORT_SUPPORT_MESSAGE)
-                .setAdmin(caller.getComponentName())
+                .setAdmin(who)
                 .write();
     }
 
@@ -12101,9 +12178,8 @@
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who, getCallerIdentity().getUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.shortSupportMessage;
         }
     }
@@ -12114,18 +12190,17 @@
             return;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
-        final CallerIdentity caller = getCallerIdentity();
+        final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who, caller.getUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             if (!TextUtils.equals(admin.longSupportMessage, message)) {
                 admin.longSupportMessage = message;
-                saveSettingsLocked(caller.getUserId());
+                saveSettingsLocked(userHandle);
             }
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_LONG_SUPPORT_MESSAGE)
-                .setAdmin(caller.getComponentName())
+                .setAdmin(who)
                 .write();
     }
 
@@ -12135,9 +12210,8 @@
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getActiveAdminForUidLocked(who, getCallerIdentity().getUid());
+            ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.longSupportMessage;
         }
     }
@@ -12148,9 +12222,7 @@
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query support message for user");
+        enforceSystemCaller("query support message for user");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -12167,9 +12239,7 @@
             return null;
         }
         Objects.requireNonNull(who, "ComponentName is null");
-
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query support message for user");
+        enforceSystemCaller("query support message for user");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
@@ -12296,10 +12366,7 @@
         if (!mHasFeature) {
             return null;
         }
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller));
-
+        enforceDeviceOwnerOrManageUsers();
         synchronized (getLockObject()) {
             final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
             return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName;
@@ -12396,13 +12463,12 @@
     @Override
     public boolean isMeteredDataDisabledPackageForUser(ComponentName who,
             String packageName, int userId) {
+        Objects.requireNonNull(who);
+
         if (!mHasFeature) {
             return false;
         }
-        Objects.requireNonNull(who, "ComponentName is null");
-
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query restricted pkgs for a specific user");
+        enforceSystemCaller("query restricted pkgs for a specific user");
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
@@ -12415,27 +12481,32 @@
 
     @Override
     public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
-        if (!mHasFeature) {
-            return;
-        }
         // As the caller is the system, it must specify the component name of the profile owner
         // as a sanity / safety check.
         Objects.requireNonNull(who);
 
-        final CallerIdentity caller = getCallerIdentity();
+        if (!mHasFeature) {
+            return;
+        }
+
         // Only adb or system apps with the right permission can mark a profile owner on
         // organization-owned device.
-        Preconditions.checkCallAuthorization(isAdb(caller)
-                        || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED),
-                "Only the system can mark a profile owner of organization-owned device.");
-        if (isAdb(caller)) {
-            Preconditions.checkCallAuthorization(
-                    !hasIncompatibleAccountsOrNonAdbNoLock(userId, caller),
-                    "Can only be called from ADB if the device has no accounts.");
+        if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
+            throw new SecurityException(
+                    "Only the system can mark a profile owner of organization-owned device.");
+        }
+
+        if (isAdb()) {
+            if (hasIncompatibleAccountsOrNonAdbNoLock(userId, who)) {
+                throw new SecurityException(
+                        "Can only be called from ADB if the device has no accounts.");
+            }
         } else {
-            Preconditions.checkState(!hasUserSetupCompleted(UserHandle.USER_SYSTEM),
-                    "Cannot mark profile owner as managing an organization-owned device after "
-                            + "set-up");
+            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
+                throw new IllegalStateException(
+                        "Cannot mark profile owner as managing an organization-owned device after"
+                                + " set-up");
+            }
         }
 
         // Grant access under lock.
@@ -12654,13 +12725,13 @@
         if (!mHasFeature) {
             return false;
         }
-        final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin);
 
         synchronized (getLockObject()) {
-            if (!isSystemUid(caller)) {
-                Objects.requireNonNull(admin, "ComponentName is null");
-                Preconditions.checkCallAuthorization(isDeviceOwner(caller)
-                        || isProfileOwnerOfOrganizationOwnedDevice(caller));
+            if (!isCallerWithSystemUid()) {
+                Objects.requireNonNull(admin);
+                final CallerIdentity caller = getCallerIdentity(admin);
+                Preconditions.checkCallAuthorization(
+                        isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller));
             }
             return mInjector.securityLogGetLoggingEnabledProperty();
         }
@@ -12744,15 +12815,21 @@
 
     @Override
     public long forceSecurityLogs() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isAdb(caller),
-                "Non-shell user attempted to call forceSecurityLogs");
-        Preconditions.checkState(mInjector.securityLogGetLoggingEnabledProperty(),
-                "logging is not available");
-
+        enforceShell("forceSecurityLogs");
+        if (!mInjector.securityLogGetLoggingEnabledProperty()) {
+            throw new IllegalStateException("logging is not available");
+        }
         return mSecurityLogMonitor.forceLogs();
     }
 
+    private void enforceCallerSystemUserHandle() {
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+        if (userId != UserHandle.USER_SYSTEM) {
+            throw new SecurityException("Caller has to be in user 0");
+        }
+    }
+
     @Override
     public boolean isUninstallInQueue(final String packageName) {
         final CallerIdentity caller = getCallerIdentity();
@@ -12770,21 +12847,22 @@
         Preconditions.checkArgument(!TextUtils.isEmpty(packageName));
 
         final CallerIdentity caller = getCallerIdentity();
-        final int userId = caller.getUserId();
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-        Preconditions.checkState(mUserManager.isUserUnlocked(userId),
-                "User must be running and unlocked");
+
+        final int userId = caller.getUserId();
+        enforceUserUnlocked(userId);
 
         final ComponentName profileOwner = getProfileOwner(userId);
-        Preconditions.checkArgument(
-                profileOwner == null || !packageName.equals(profileOwner.getPackageName()),
-                "Cannot uninstall a package with a profile owner");
+        if (profileOwner != null && packageName.equals(profileOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a profile owner");
+        }
 
         final ComponentName deviceOwner = getDeviceOwnerComponent(/* callingUserOnly= */ false);
-        Preconditions.checkArgument(deviceOwner == null || getDeviceOwnerUserId() != userId
-                || !packageName.equals(deviceOwner.getPackageName()),
-                "Cannot uninstall a package with a device owner");
+        if (getDeviceOwnerUserId() == userId && deviceOwner != null
+                && packageName.equals(deviceOwner.getPackageName())) {
+            throw new IllegalArgumentException("Cannot uninstall a package with a device owner");
+        }
 
         final Pair<String, Integer> packageUserPair = new Pair<>(packageName, userId);
         synchronized (getLockObject()) {
@@ -12935,24 +13013,22 @@
      */
     @Override
     public void forceUpdateUserSetupComplete() {
-        final CallerIdentity caller = getCallerIdentity();
-
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
-        Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
-                "Caller has to be in user 0");
+        enforceCallerSystemUserHandle();
 
         // no effect if it's called from user build
         if (!mInjector.isBuildDebuggable()) {
             return;
         }
+        final int userId = UserHandle.USER_SYSTEM;
         boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
-                Settings.Secure.USER_SETUP_COMPLETE, 0, caller.getUserId()) != 0;
-        DevicePolicyData policy = getUserData(caller.getUserId());
+                Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
+        DevicePolicyData policy = getUserData(userId);
         policy.mUserSetupComplete = isUserCompleted;
         mStateCache.setDeviceProvisioned(isUserCompleted);
         synchronized (getLockObject()) {
-            saveSettingsLocked(caller.getUserId());
+            saveSettingsLocked(userId);
         }
     }
 
@@ -13107,8 +13183,9 @@
      *
      * DO NOT CALL IT WITH THE DPMS LOCK HELD.
      */
-    private boolean hasIncompatibleAccountsOrNonAdbNoLock(int userId, CallerIdentity caller) {
-        if (!isAdb(caller)) {
+    private boolean hasIncompatibleAccountsOrNonAdbNoLock(
+            int userId, @Nullable ComponentName owner) {
+        if (!isAdb()) {
             return true;
         }
         wtfIfInLock();
@@ -13120,8 +13197,7 @@
                 return false;
             }
             synchronized (getLockObject()) {
-                if (caller.getComponentName() == null
-                        || !isAdminTestOnlyLocked(caller.getComponentName(), userId)) {
+                if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
                     Log.w(LOG_TAG,
                             "Non test-only owner can't be installed with existing accounts.");
                     return true;
@@ -13164,8 +13240,9 @@
         }
     }
 
-    private boolean isAdb(CallerIdentity caller) {
-        return isShellUid(caller) || isRootUid(caller);
+    private boolean isAdb() {
+        final int callingUid = mInjector.binderGetCallingUid();
+        return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
     }
 
     @Override
@@ -13227,13 +13304,11 @@
 
     @Override
     public long forceNetworkLogs() {
-        Preconditions.checkCallAuthorization(isAdb(getCallerIdentity()),
-                "Non-shell user attempted to call forceNetworkLogs");
-
+        enforceShell("forceNetworkLogs");
         synchronized (getLockObject()) {
-            Preconditions.checkState(isNetworkLoggingEnabledInternalLocked(),
-                    "logging is not available");
-
+            if (!isNetworkLoggingEnabledInternalLocked()) {
+                throw new IllegalStateException("logging is not available");
+            }
             if (mNetworkLogger != null) {
                 return mInjector.binderWithCleanCallingIdentity(
                         () -> mNetworkLogger.forceBatchFinalization());
@@ -13433,28 +13508,19 @@
 
     @Override
     public long getLastSecurityLogRetrievalTime() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller));
-
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime;
      }
 
     @Override
     public long getLastBugReportRequestTime() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller));
-
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime;
      }
 
     @Override
     public long getLastNetworkLogRetrievalTime() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(
-                isCallerDeviceOwner(caller.getUid()) || canManageUsers(caller));
-
+        enforceDeviceOwnerOrManageUsers();
         return getUserData(UserHandle.USER_SYSTEM).mLastNetworkLogsRetrievalTime;
     }
 
@@ -13534,18 +13600,16 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        Objects.requireNonNull(admin);
         Objects.requireNonNull(token);
-
-        final CallerIdentity caller = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
-
         synchronized (getLockObject()) {
-            DevicePolicyData policy = getUserData(caller.getUserId());
+            final int userHandle = mInjector.userHandleGetCallingUserId();
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            DevicePolicyData policy = getUserData(userHandle);
             if (policy.mPasswordTokenHandle != 0) {
                 final String password = passwordOrNull != null ? passwordOrNull : "";
                 return resetPasswordInternal(password, policy.mPasswordTokenHandle, token,
-                        flags, caller.getUid());
+                        flags, mInjector.binderGetCallingUid());
             } else {
                 Slog.w(LOG_TAG, "No saved token handle");
             }
@@ -13555,21 +13619,15 @@
 
     @Override
     public boolean isCurrentInputMethodSetByOwner() {
-        final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
-                || isCallerProfileOwner(caller.getUid()) || isSystemUid(caller));
-
-        return getUserData(caller.getUserId()).mCurrentInputMethodSet;
+        enforceProfileOwnerOrSystemUser();
+        return getUserData(mInjector.userHandleGetCallingUserId()).mCurrentInputMethodSet;
     }
 
     @Override
     public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) {
         final int userId = user.getIdentifier();
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
-                || isCallerProfileOwner(caller.getUid())
-                || hasFullCrossUsersPermission(caller, userId));
-
+        enforceProfileOwnerOrFullCrossUsersPermission(caller, userId);
         synchronized (getLockObject()) {
             return new StringParceledListSlice(
                     new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts));
@@ -14456,12 +14514,14 @@
         }
         Preconditions.checkStringNotEmpty(packageName, "Package name is empty");
 
-        final CallerIdentity caller = getCallerIdentity(packageName);
-        Preconditions.checkCallAuthorization(isCallingFromPackage(packageName, caller.getUid()),
-                "Input package name doesn't align with actual calling package.");
-
+        final int callingUid = mInjector.binderGetCallingUid();
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        if (!isCallingFromPackage(packageName, callingUid)) {
+            throw new SecurityException("Input package name doesn't align with actual "
+                    + "calling package.");
+        }
         return mInjector.binderWithCleanCallingIdentity(() -> {
-            final int workProfileUserId = getManagedUserId(caller.getUserId());
+            final int workProfileUserId = getManagedUserId(callingUserId);
             if (workProfileUserId < 0) {
                 return false;
             }
@@ -14945,9 +15005,7 @@
 
     @Override
     public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
-        Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()),
-                "Only the system can query profile owner can reset password when locked");
-
+        enforceSystemCaller("call canProfileOwnerResetPasswordWhenLocked");
         synchronized (getLockObject()) {
             final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
             if (poAdmin == null
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 8b1e9c5..91cb481 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -30,6 +30,7 @@
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -73,7 +74,7 @@
 
     @Override
     public void onStart() {
-        publishBinderService(Context.PEOPLE_SERVICE, new BinderService());
+        publishBinderService(Context.PEOPLE_SERVICE, mService);
         publishLocalService(PeopleServiceInternal.class, new LocalService());
     }
 
@@ -117,7 +118,7 @@
                 message);
     }
 
-    private final class BinderService extends IPeopleManager.Stub {
+    final IBinder mService = new IPeopleManager.Stub() {
 
         @Override
         public ParceledListSlice<ConversationChannel> getRecentConversations() {
@@ -146,7 +147,7 @@
             enforceSystemRootOrSystemUI(getContext(), "get last interaction");
             return mDataManager.getLastInteraction(packageName, userId, shortcutId);
         }
-    }
+    };
 
     @VisibleForTesting
     final class LocalService extends PeopleServiceInternal {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index be258dc..31ec4a5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -66,6 +66,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
@@ -111,6 +112,7 @@
     private static final CallerIdentity IDENTITY = CallerIdentity.forTest(CURRENT_USER, 1,
             "mypackage",
             "attribution");
+    private static final WorkSource WORK_SOURCE = new WorkSource(IDENTITY.getUid());
 
     private Random mRandom;
 
@@ -202,22 +204,20 @@
     @Test
     public void testIsEnabled() {
         assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
 
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
         assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
 
         mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
         mProvider.setAllowed(false);
         assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
 
         mProvider.setAllowed(true);
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        assertThat(mManager.isEnabled(CURRENT_USER)).isFalse();
-        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
-
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
         assertThat(mManager.isEnabled(CURRENT_USER)).isTrue();
-        assertThat(mManager.isEnabled(OTHER_USER)).isFalse();
+        assertThat(mManager.isEnabled(OTHER_USER)).isTrue();
     }
 
     @Test
@@ -237,23 +237,15 @@
         mProvider.setAllowed(false);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                 false);
+        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
+                false);
 
         mProvider.setAllowed(true);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, CURRENT_USER,
                 true);
-
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
-                false);
         verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
                 true);
 
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, CURRENT_USER,
-                true);
-        verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, OTHER_USER,
-                false);
-
         mManager.removeEnabledListener(listener);
         mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
         verifyNoMoreInteractions(listener);
@@ -343,7 +335,7 @@
     @Test
     public void testPassive_Listener() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mPassive.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
@@ -368,8 +360,11 @@
         ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                IDENTITY,
+                PERMISSION_FINE,
+                listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -397,16 +392,6 @@
         mProvider.setAllowed(true);
         verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, true);
 
-        mInjector.getUserInfoHelper().setCurrentUserId(OTHER_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, false);
-        loc = createLocation(NAME, mRandom);
-        mProvider.setProviderLocation(loc);
-        verify(listener, times(1)).onLocationChanged(any(Location.class),
-                nullable(IRemoteCallback.class));
-
-        mInjector.getUserInfoHelper().setCurrentUserId(CURRENT_USER);
-        verify(listener, timeout(TIMEOUT_MS).times(3)).onProviderEnabledChanged(NAME, true);
-
         loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
         verify(listener, times(2)).onLocationChanged(locationCaptor.capture(),
@@ -422,8 +407,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -435,8 +423,11 @@
     @Test
     public void testRegisterListener_Unregister() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), IDENTITY,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                IDENTITY,
+                PERMISSION_FINE,
+                listener);
         mManager.unregisterLocationRequest(listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -453,8 +444,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
         IN_PROCESS_EXECUTOR.execute(() -> {
@@ -475,7 +469,10 @@
     @Test
     public void testRegisterListener_NumUpdates() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setMaxUpdates(5).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setMaxUpdates(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -492,7 +489,10 @@
     @Test
     public void testRegisterListener_ExpiringAlarm() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(5000).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setDurationMillis(5000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAlarmHelper().incrementAlarmTime(5000);
@@ -504,7 +504,10 @@
     @Test
     public void testRegisterListener_ExpiringNoAlarm() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).setDurationMillis(25).build();
+        LocationRequest request = new LocationRequest.Builder(0)
+                .setDurationMillis(25)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Thread.sleep(25);
@@ -517,8 +520,10 @@
     @Test
     public void testRegisterListener_FastestInterval() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateIntervalMillis(
-                5000).build();
+        LocationRequest request = new LocationRequest.Builder(5000)
+                .setMinUpdateIntervalMillis(5000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -531,8 +536,10 @@
     @Test
     public void testRegisterListener_SmallestDisplacement() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5000).setMinUpdateDistanceMeters(
-                1f).build();
+        LocationRequest request = new LocationRequest.Builder(5000)
+                .setMinUpdateDistanceMeters(1f)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
@@ -546,7 +553,7 @@
     @Test
     public void testRegisterListener_NoteOpFailure() throws Exception {
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, IDENTITY.getPackageName(),
@@ -564,8 +571,11 @@
                 "attribution");
 
         ILocationListener listener = createMockLocationListener();
-        mManager.registerLocationRequest(new LocationRequest.Builder(0).build(), identity,
-                PERMISSION_FINE, listener);
+        mManager.registerLocationRequest(
+                new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build(),
+                identity,
+                PERMISSION_FINE,
+                listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
         IN_PROCESS_EXECUTOR.execute(() -> {
@@ -592,8 +602,8 @@
         ArgumentCaptor<Location> locationCaptor = ArgumentCaptor.forClass(Location.class);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         Location loc = createLocation(NAME, mRandom);
         mProvider.setProviderLocation(loc);
@@ -606,8 +616,8 @@
     @Test
     public void testGetCurrentLocation_Cancel() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        ICancellationSignal cancellationSignal = mManager.getCurrentLocation(locationRequest,
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        ICancellationSignal cancellationSignal = mManager.getCurrentLocation(request,
                 IDENTITY, PERMISSION_FINE, listener);
 
         cancellationSignal.cancel();
@@ -619,8 +629,8 @@
     @Test
     public void testGetCurrentLocation_ProviderDisabled() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderAllowed(false);
         mProvider.setProviderAllowed(true);
@@ -633,8 +643,8 @@
         mProvider.setProviderAllowed(false);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mProvider.setProviderAllowed(true);
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
@@ -649,8 +659,8 @@
         mProvider.setProviderLocation(loc);
 
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         verify(listener, times(1)).onLocation(locationCaptor.capture());
         assertThat(locationCaptor.getValue()).isEqualTo(loc);
@@ -659,8 +669,8 @@
     @Test
     public void testGetCurrentLocation_Timeout() throws Exception {
         ILocationCallback listener = createMockGetCurrentLocationListener();
-        LocationRequest locationRequest = new LocationRequest.Builder(0).build();
-        mManager.getCurrentLocation(locationRequest, IDENTITY, PERMISSION_FINE, listener);
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
+        mManager.getCurrentLocation(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getAlarmHelper().incrementAlarmTime(60000);
         verify(listener, times(1)).onLocation(isNull());
@@ -674,7 +684,7 @@
                 IDENTITY.getPackageName())).isFalse();
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(0).build();
+        LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         assertThat(mInjector.getAppOpsHelper().isAppOpStarted(OP_MONITOR_LOCATION,
@@ -703,7 +713,8 @@
         assertThat(mProvider.getRequest().getLocationRequests()).isEmpty();
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource(
+                WORK_SOURCE).build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -714,7 +725,10 @@
         assertThat(mProvider.getRequest().getWorkSource()).isNotNull();
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(1).setLowPower(true).build();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setLowPower(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -745,7 +759,9 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(60000).build();
+        LocationRequest request1 = new LocationRequest.Builder(60000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         verify(listener1).onLocationChanged(any(Location.class), nullable(IRemoteCallback.class));
@@ -762,7 +778,9 @@
         mProvider.setProviderLocation(createLocation(NAME, mRandom));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(60000).build();
+        LocationRequest request1 = new LocationRequest.Builder(60000)
+                .setWorkSource(WORK_SOURCE)
+                .build();
 
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
         assertThat(mProvider.getRequest().isActive()).isFalse();
@@ -777,7 +795,9 @@
     @Test
     public void testProviderRequest_BackgroundThrottle() {
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
@@ -793,7 +813,9 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -801,8 +823,10 @@
         assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse();
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(1).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request2 = new LocationRequest.Builder(1)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
@@ -816,12 +840,16 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(1).build();
+        LocationRequest request1 = new LocationRequest.Builder(1)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         ILocationListener listener2 = createMockLocationListener();
-        LocationRequest request2 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request2 = new LocationRequest.Builder(5)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
 
         mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
@@ -838,8 +866,10 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(1).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request = new LocationRequest.Builder(1)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         mInjector.getSettingsHelper().setIgnoreSettingsPackageWhitelist(Collections.emptySet());
@@ -855,8 +885,10 @@
                 Collections.singleton(IDENTITY.getPackageName()));
 
         ILocationListener listener1 = createMockLocationListener();
-        LocationRequest request1 = new LocationRequest.Builder(5).setLocationSettingsIgnored(
-                true).build();
+        LocationRequest request1 = new LocationRequest.Builder(5)
+                .setLocationSettingsIgnored(true)
+                .setWorkSource(WORK_SOURCE)
+                .build();
         mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
 
         assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
@@ -871,7 +903,7 @@
                 LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
 
         ILocationListener listener = createMockLocationListener();
-        LocationRequest request = new LocationRequest.Builder(5).build();
+        LocationRequest request = new LocationRequest.Builder(5).setWorkSource(WORK_SOURCE).build();
         mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, listener);
 
         assertThat(mProvider.getRequest().isActive()).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
index 9ee9259..292b7c6 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -77,6 +77,7 @@
     private TestThreadingDomain mTestThreadingDomain;
     private TestCallback mTestCallback;
     private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
+    private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
 
     @Before
     public void setUp() {
@@ -87,26 +88,30 @@
         mTestCallback = new TestCallback(mTestThreadingDomain);
         mTestPrimaryLocationTimeZoneProvider =
                 new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
+        mTestSecondaryLocationTimeZoneProvider =
+                new TestLocationTimeZoneProvider(mTestThreadingDomain, "secondary");
     }
 
     @Test
     public void initialState_enabled() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
         Duration expectedInitTimeout = testEnvironment.getProviderInitializationTimeout()
                 .plus(testEnvironment.getProviderInitializationTimeoutFuzz());
 
-        // Initialize. After initialization the provider must be initialized and should be
+        // Initialize. After initialization the providers must be initialized and one should be
         // enabled.
         controllerImpl.initialize(testEnvironment, mTestCallback);
 
         mTestPrimaryLocationTimeZoneProvider.assertInitialized();
+        mTestSecondaryLocationTimeZoneProvider.assertInitialized();
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -114,17 +119,19 @@
     @Test
     public void initialState_disabled() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
 
-        // Initialize. After initialization the provider must be initialized but should not be
+        // Initialize. After initialization the providers must be initialized but neither should be
         // enabled.
         controllerImpl.initialize(testEnvironment, mTestCallback);
 
         mTestPrimaryLocationTimeZoneProvider.assertInitialized();
+        mTestSecondaryLocationTimeZoneProvider.assertInitialized();
 
         mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -132,7 +139,7 @@
     @Test
     public void enabled_uncertaintySuggestionSentIfNoEventReceived() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -141,6 +148,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -148,9 +156,24 @@
         mTestThreadingDomain.executeNext();
 
         // The primary should have reported uncertainty, which should trigger the controller to
-        // start the uncertainty timeout.
+        // start the uncertainty timeout and enable the secondary.
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate time passing with no provider event being received from either the primary or
+        // secondary.
+        mTestThreadingDomain.executeNext();
+
+        // Now both initialization timeouts should have triggered. The uncertainty timeout should
+        // still not be triggered.
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertNoSuggestionMade();
         assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
 
@@ -160,6 +183,8 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertUncertainSuggestionMadeAndCommit();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -167,7 +192,7 @@
     @Test
     public void enabled_eventReceivedBeforeInitializationTimeout() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -176,6 +201,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -186,6 +212,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -194,7 +221,7 @@
     @Test
     public void enabled_eventReceivedFromPrimaryAfterInitializationTimeout() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -203,6 +230,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -211,16 +239,59 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertNoSuggestionMade();
         assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
 
         // Simulate a location event being received from the primary provider. This should cause a
-        // suggestion to be made.
+        // suggestion to be made and the secondary to be shut down.
         mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+    }
+
+    @Test
+    public void enabled_eventReceivedFromSecondaryAfterInitializationTimeout() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate time passing with no provider event being received from the primary.
+        mTestThreadingDomain.executeNext();
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate a location event being received from the secondary provider. This should cause a
+        // suggestion to be made.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -229,7 +300,7 @@
     @Test
     public void enabled_repeatedPrimaryCertainty() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -238,6 +309,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -248,6 +320,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -258,6 +331,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -267,15 +341,16 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
     @Test
-    public void enabled_uncertaintyTriggersASuggestionAfterUncertaintyTimeout() {
+    public void enabled_repeatedSecondaryCertainty() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -284,6 +359,70 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate time passing with no provider event being received from the primary.
+        mTestThreadingDomain.executeNext();
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate a location event being received from the secondary provider. This should cause a
+        // suggestion to be made.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // A second, identical event should not cause another suggestion.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // And a third, different event should cause another suggestion.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+    }
+
+    @Test
+    public void enabled_uncertaintyTriggersASuggestionAfterUncertaintyTimeout() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -294,18 +433,48 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate an uncertain event being received from the primary provider. This should not
         // cause a suggestion to be made straight away, but the uncertainty timeout should be
-        // started.
+        // started and the secondary should be enabled.
         mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
                 USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate a location event being received from the secondary provider. This should cause a
+        // suggestion to be made, cancel the uncertainty timeout and ensure the secondary is
+        // considered initialized.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate an uncertain event being received from the secondary provider. This should not
+        // cause a suggestion to be made straight away, but the uncertainty timeout should be
+        // started. Both providers are now enabled, with no initialization timeout set.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertNoSuggestionMade();
         assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
 
@@ -315,6 +484,8 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertUncertainSuggestionMadeAndCommit();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -322,7 +493,7 @@
     @Test
     public void enabled_briefUncertaintyTriggersNoSuggestion() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -331,6 +502,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -341,27 +513,32 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Uncertainty should not cause a suggestion to be made straight away, but the uncertainty
-        // timeout should be started.
+        // timeout should be started and the secondary should be enabled.
         mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
                 USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
         mTestCallback.assertNoSuggestionMade();
         assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
 
         // And a success event from the primary provider should cause the controller to make another
-        // suggestion, the uncertainty timeout should be cancelled.
+        // suggestion, the uncertainty timeout should be cancelled and the secondary should be
+        // disabled again.
         mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -370,7 +547,7 @@
     @Test
     public void configChanges_enableAndDisableWithNoPreviousSuggestion() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
 
@@ -378,6 +555,7 @@
         controllerImpl.initialize(testEnvironment, mTestCallback);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -386,6 +564,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -393,6 +572,7 @@
         testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -400,7 +580,7 @@
     @Test
     public void configChanges_enableAndDisableWithPreviousSuggestion() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
 
@@ -408,6 +588,7 @@
         controllerImpl.initialize(testEnvironment, mTestCallback);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -416,6 +597,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -425,6 +607,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -436,6 +619,7 @@
         testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertUncertainSuggestionMadeAndCommit();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
@@ -443,7 +627,7 @@
     @Test
     public void configChanges_userSwitch_enabledToEnabled() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -452,6 +636,7 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -463,6 +648,7 @@
         // and also clear the scheduled uncertainty suggestion.
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertSuggestionMadeAndCommit(
                 USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -477,13 +663,14 @@
         mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
 
     @Test
-    public void primaryPermFailure_disableAndEnable() {
+    public void primaryPermFailure_secondaryEventsReceived() {
         ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
-                mTestPrimaryLocationTimeZoneProvider);
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
         TestEnvironment testEnvironment = new TestEnvironment(
                 mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
 
@@ -492,22 +679,86 @@
 
         mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
                 PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Simulate a failure location event being received from the primary provider. This should
-        // cause an uncertain suggestion to be made.
+        // cause the secondary to be enabled.
         mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
                 USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
-        mTestCallback.assertUncertainSuggestionMadeAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate uncertainty from the secondary.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // And a success event from the secondary provider should cause the controller to make
+        // another suggestion, the uncertainty timeout should be cancelled.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate uncertainty from the secondary.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+    }
+
+    @Test
+    public void primaryPermFailure_disableAndEnable() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate a failure location event being received from the primary provider. This should
+        // cause the secondary to be enabled.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
         // Now signal a config change so that geo detection is disabled.
         testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
         mTestCallback.assertNoSuggestionMade();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
 
@@ -515,6 +766,164 @@
         testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
 
         mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+    }
+
+    @Test
+    public void secondaryPermFailure_primaryEventsReceived() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate an uncertain event from the primary. This will enable the secondary, which will
+        // give this test the opportunity to simulate its failure. Then it will be possible to
+        // demonstrate controller behavior with only the primary working.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate failure event from the secondary. This should just affect the secondary's state.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // And a success event from the primary provider should cause the controller to make
+        // a suggestion, the uncertainty timeout should be cancelled.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertSuggestionMadeAndCommit(
+                USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate uncertainty from the primary. The secondary cannot be enabled.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+    }
+
+    @Test
+    public void secondaryPermFailure_disableAndEnable() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate an uncertain event from the primary. This will enable the secondary, which will
+        // give this test the opportunity to simulate its failure. Then it will be possible to
+        // demonstrate controller behavior with only the primary working.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Simulate failure event from the secondary. This should just affect the secondary's state.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
+
+        // Now signal a config change so that geo detection is disabled.
+        testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Now signal a config change so that geo detection is enabled. Only the primary can be
+        // enabled.
+        testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+    }
+
+    @Test
+    public void bothPermFailure_disableAndEnable() {
+        ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
+                mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
+        TestEnvironment testEnvironment = new TestEnvironment(
+                mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_ENABLED);
+
+        // Initialize and check initial state.
+        controllerImpl.initialize(testEnvironment, mTestCallback);
+
+        mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate a failure event from the primary. This will enable the secondary.
+        mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
+                PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
+        mTestCallback.assertNoSuggestionMade();
+        assertFalse(controllerImpl.isUncertaintyTimeoutSet());
+
+        // Simulate failure event from the secondary.
+        mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
+                USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
+
+        mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
+        mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
         mTestCallback.assertUncertainSuggestionMadeAndCommit();
         assertFalse(controllerImpl.isUncertaintyTimeoutSet());
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
new file mode 100644
index 0000000..62e135b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.PackageManager;
+import android.os.ConditionVariable;
+import android.os.incremental.IStorageHealthListener;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit tests for {@link IncrementalStates}.
+ * Run with: atest -c FrameworksServicesTests:com.android.server.pm.IncrementalStatesTest
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class IncrementalStatesTest {
+    private IncrementalStates mIncrementalStates;
+    private ConditionVariable mUnstartableCalled = new ConditionVariable();
+    private ConditionVariable mStartableCalled = new ConditionVariable();
+    private ConditionVariable mFullyLoadedCalled = new ConditionVariable();
+    private AtomicInteger mUnstartableReason = new AtomicInteger(0);
+    private static final int WAIT_TIMEOUT_MILLIS = 1000; /* 1 second */
+    private IncrementalStates.Callback mCallback = new IncrementalStates.Callback() {
+        @Override
+        public void onPackageUnstartable(int reason) {
+            mUnstartableCalled.open();
+            mUnstartableReason.set(reason);
+        }
+
+        @Override
+        public void onPackageStartable() {
+            mStartableCalled.open();
+        }
+
+        @Override
+        public void onPackageFullyLoaded() {
+            mFullyLoadedCalled.open();
+        }
+    };
+
+    /**
+     * Setup the tests as if the package has just been committed.
+     * By default the package is now startable and is loading.
+     */
+    @Before
+    public void setUp() {
+        mIncrementalStates = new IncrementalStates();
+        assertFalse(mIncrementalStates.isStartable());
+        mIncrementalStates.setCallback(mCallback);
+        mIncrementalStates.onCommit(true);
+        // Test that package is now startable and loading
+        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+        assertTrue(mIncrementalStates.isLoading());
+        mStartableCalled.close();
+        mUnstartableCalled.close();
+        mFullyLoadedCalled.close();
+    }
+
+    /**
+     * Test that startable state changes to false when Incremental Storage is unhealthy.
+     */
+    @Test
+    public void testStartableTransition_IncrementalStorageUnhealthy() {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
+    }
+
+    /**
+     * Test that the package is still startable when Incremental Storage has pending reads.
+     */
+    @Test
+    public void testStartableTransition_IncrementalStorageReadsPending()
+            throws InterruptedException {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that the package is still startable when Incremental Storage is at blocked status.
+     */
+    @Test
+    public void testStartableTransition_IncrementalStorageBlocked() {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_BLOCKED);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that the package is still startable when Data Loader has unknown transportation issues.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderTransportError() {
+        mIncrementalStates.onStreamStatusChanged(
+                IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Data Loader has data integrity issues.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderIntegrityError() {
+        mIncrementalStates.onStreamStatusChanged(
+                IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
+                mUnstartableReason.get());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Data Loader has data source issues.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderSourceError() {
+        mIncrementalStates.onStreamStatusChanged(
+                IDataLoaderStatusListener.STREAM_SOURCE_ERROR);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
+                mUnstartableReason.get());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Data Loader hits limited storage while
+     * Incremental storage has a pending reads.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStoragePending()
+            throws InterruptedException {
+        mIncrementalStates.onStreamStatusChanged(
+                IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE,
+                mUnstartableReason.get());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Data Loader hits limited storage while
+     * Incremental storage is at blocked status.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStorageBlocked()
+            throws InterruptedException {
+        mIncrementalStates.onStreamStatusChanged(
+                IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_BLOCKED);
+        // Test that package is now unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE,
+                mUnstartableReason.get());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Incremental Storage is unhealthy, and it
+     * becomes startable again when Incremental Storage is healthy again.
+     */
+    @Test
+    public void testStartableTransition_IncrementalStorageUnhealthyBackToHealthy()
+            throws InterruptedException {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
+        // Test that package is unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_OK);
+        // Test that package is now startable
+        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that the package becomes unstartable when Data Loader has data integrity issue, and it
+     * becomes startable again when Data Loader is healthy again.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderUnhealthyBackToHealthy()
+            throws InterruptedException {
+        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+        // Test that package is unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+
+        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
+        // Test that package is now startable
+        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that the package becomes unstartable when both Incremental Storage and Data Loader
+     * are unhealthy, and it becomes startable again when both Incremental Storage and Data Loader
+     * are healthy again.
+     */
+    @Test
+    public void testStartableTransition_DataLoaderAndIncrementalStorageUnhealthyBackToHealthy()
+            throws InterruptedException {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
+        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+        // Test that package is unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+
+        mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
+        // Test that package is still unstartable
+        assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK);
+        // Test that package is now startable
+        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that when loading progress is 1, the package becomes fully loaded, and the change of
+     * Incremental Storage health status does not affect the startable state.
+     */
+    @Test
+    public void testStartableTransition_HealthStatusChangeWhenFullyLoaded()
+            throws InterruptedException {
+        mIncrementalStates.setProgress(1.0f);
+        // Test that package is now fully loaded
+        assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isLoading());
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
+        // Test that package is still startable
+        assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+    }
+
+    /**
+     * Test that when loading progress is 1, the package becomes fully loaded, and if the package
+     * was unstartable, it becomes startable.
+     */
+    @Test
+    public void testLoadingTransition_FullyLoadedWhenUnstartable() throws InterruptedException {
+        mIncrementalStates.onStorageHealthStatusChanged(
+                IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
+        // Test that package is unstartable
+        assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        // Test that package is still loading
+        assertTrue(mIncrementalStates.isLoading());
+
+        mIncrementalStates.setProgress(0.5f);
+        // Test that package is still unstartable
+        assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isStartable());
+        mIncrementalStates.setProgress(1.0f);
+        // Test that package is now startable
+        assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertTrue(mIncrementalStates.isStartable());
+        // Test that package is now fully loaded
+        assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
+        assertFalse(mIncrementalStates.isLoading());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index bc74783..6255630 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -891,8 +891,10 @@
             assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
         }
         assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
-        assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
-        assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
+        assertNotSame(origPkgSetting.mLegacyPermissionsState,
+                testPkgSetting.mLegacyPermissionsState);
+        assertThat(origPkgSetting.mLegacyPermissionsState,
+                is(testPkgSetting.mLegacyPermissionsState));
         assertThat(origPkgSetting.name, is(testPkgSetting.name));
         // mOldCodePaths is _not_ copied
         // assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index e3c795d..9099272 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -39,6 +39,8 @@
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
     <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index cf07183..f026b85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -300,15 +300,15 @@
     }
 
     @Test
-    public void testRemoveStackInWindowingModes() {
-        removeStackTests(() -> mRootWindowContainer.removeStacksInWindowingModes(
+    public void testRemoveRootTaskInWindowingModes() {
+        removeStackTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
                 WINDOWING_MODE_FULLSCREEN));
     }
 
     @Test
     public void testRemoveStackWithActivityTypes() {
-        removeStackTests(
-                () -> mRootWindowContainer.removeStacksWithActivityTypes(ACTIVITY_TYPE_STANDARD));
+        removeStackTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
+                ACTIVITY_TYPE_STANDARD));
     }
 
     private void removeStackTests(Runnable runnable) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 0311657..0f80561 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -160,7 +160,7 @@
         };
         mAtm.mWindowManager.registerDisplayWindowListener(listener);
         // Check that existing displays call added
-        assertEquals(1, added.size());
+        assertEquals(mRootWindowContainer.getChildCount(), added.size());
         assertEquals(0, changed.size());
         assertEquals(0, removed.size());
         added.clear();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
new file mode 100644
index 0000000..9a668b9
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link DisplayAreaGroup} container.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:DisplayAreaGroupTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class DisplayAreaGroupTest extends WindowTestsBase {
+
+    private DisplayAreaGroup mDisplayAreaGroup;
+    private TaskDisplayArea mTaskDisplayArea;
+    private Task mStack;
+    private ActivityRecord mActivity;
+
+    @Before
+    public void setUp() {
+        mDisplayAreaGroup = new DisplayAreaGroup(
+                mWm, "DisplayAreaGroup", FEATURE_VENDOR_FIRST);
+        final TaskDisplayArea defaultTda = mDisplayContent.getDefaultTaskDisplayArea();
+        final WindowContainer parentDA = defaultTda.getParent();
+        parentDA.addChild(mDisplayAreaGroup, parentDA.mChildren.indexOf(defaultTda) + 1);
+        mTaskDisplayArea = new TaskDisplayArea(
+                mDisplayContent, mWm, "TDA1", FEATURE_VENDOR_FIRST + 1);
+        mDisplayAreaGroup.addChild(mTaskDisplayArea, POSITION_TOP);
+        mStack = mTaskDisplayArea.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mActivity = new ActivityBuilder(mAtm).setCreateTask(true).setStack(mStack).build();
+        mDisplayContent.setLastFocusedTaskDisplayArea(mTaskDisplayArea);
+    }
+
+    @Test
+    public void testIsOrientationDifferentFromDisplay() {
+        // Display is portrait, DisplayAreaGroup inherits that
+        mDisplayContent.setBounds(0, 0, 600, 900);
+
+        assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isFalse();
+
+        // DisplayAreaGroup is landscape, different Display
+        mDisplayAreaGroup.setBounds(0, 0, 600, 450);
+
+        assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isTrue();
+
+        // DisplayAreaGroup is portrait, same as Display
+        mDisplayAreaGroup.setBounds(0, 0, 300, 900);
+
+        assertThat(mDisplayAreaGroup.isOrientationDifferentFromDisplay()).isFalse();
+    }
+
+    @Test
+    public void testGetOrientation() {
+        doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any());
+        mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+
+        // Display is portrait, DisplayAreaGroup inherits that
+        mDisplayContent.setBounds(0, 0, 600, 900);
+
+        assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
+        assertThat(mActivity.getRequestedConfigurationOrientation())
+                .isEqualTo(ORIENTATION_PORTRAIT);
+
+        // DisplayAreaGroup is landscape, different from Display
+        mDisplayAreaGroup.setBounds(0, 0, 600, 450);
+
+        assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+        assertThat(mActivity.getRequestedConfigurationOrientation())
+                .isEqualTo(ORIENTATION_LANDSCAPE);
+
+        // DisplayAreaGroup is portrait, same as Display
+        mDisplayAreaGroup.setBounds(0, 0, 300, 900);
+
+        assertThat(mDisplayAreaGroup.getOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
+        assertThat(mActivity.getRequestedConfigurationOrientation())
+                .isEqualTo(ORIENTATION_PORTRAIT);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 820eca4..bc7516f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -19,6 +19,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -319,6 +320,29 @@
     }
 
     /**
+     * Ensures that {@link LaunchParamsModifier} doesn't alter non-root tasks' windowingMode.
+     */
+    @Test
+    public void testLayoutNonRootTaskWindowingModeChange() {
+        final LaunchParams params = new LaunchParams();
+        final int windowingMode = WINDOWING_MODE_FREEFORM;
+        params.mWindowingMode = windowingMode;
+        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
+        final Task task = new TaskBuilder(mAtm.mStackSupervisor).setCreateParentTask(true).build();
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        mController.registerModifier(positioner);
+
+        final int beforeWindowMode = task.getWindowingMode();
+        assertNotEquals(windowingMode, beforeWindowMode);
+
+        mController.layoutTask(task, null /* windowLayout */);
+
+        final int afterWindowMode = task.getWindowingMode();
+        assertEquals(afterWindowMode, beforeWindowMode);
+    }
+
+    /**
      * Ensures that {@link LaunchParamsModifier} requests specifying bounds during
      * layout are honored if window is in freeform.
      */
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index f4f172d..1bf83ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1154,22 +1154,22 @@
     }
 
     private void doTestRecentTasksApis(boolean expectCallable) {
-        assertSecurityException(expectCallable, () -> mAtm.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable, () -> mAtm.removeTask(INVALID_STACK_ID));
         assertSecurityException(expectCallable,
-                () -> mAtm.removeStacksInWindowingModes(
+                () -> mAtm.removeRootTasksInWindowingModes(
                         new int[]{WINDOWING_MODE_UNDEFINED}));
         assertSecurityException(expectCallable,
-                () -> mAtm.removeStacksWithActivityTypes(
+                () -> mAtm.removeRootTasksWithActivityTypes(
                         new int[]{ACTIVITY_TYPE_UNDEFINED}));
         assertSecurityException(expectCallable, () -> mAtm.removeTask(0));
         assertSecurityException(expectCallable,
                 () -> mAtm.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
         assertSecurityException(expectCallable,
-                () -> mAtm.moveTaskToStack(0, INVALID_STACK_ID, true));
+                () -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
                 () -> mAtm.setTaskWindowingModeSplitScreenPrimary(0, true));
         assertSecurityException(expectCallable,
-                () -> mAtm.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+                () -> mAtm.moveTopActivityToPinnedRootTask(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
         assertSecurityException(expectCallable,
                 () -> mAtm.getRootTaskInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
@@ -1188,7 +1188,7 @@
                 () -> mAtm.unregisterTaskStackListener(null));
         assertSecurityException(expectCallable, () -> mAtm.getTaskDescription(0));
         assertSecurityException(expectCallable, () -> mAtm.cancelTaskWindowTransition(0));
-        assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, null,
+        assertSecurityException(expectCallable, () -> mAtm.startRecentsActivity(null, 0,
                 null));
         assertSecurityException(expectCallable, () -> mAtm.cancelRecentsAnimation(true));
         assertSecurityException(expectCallable, () -> mAtm.stopAppSwitches());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index d821d38..c10d4fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -147,7 +147,7 @@
 
         Intent recentsIntent = new Intent().setComponent(mRecentsComponent);
         // Null animation indicates to preload.
-        mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
+        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
                 null /* recentsAnimationRunner */);
 
         Task recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN,
@@ -167,7 +167,7 @@
 
         spyOn(recentsActivity);
         // Start when the recents activity exists. It should ensure the configuration.
-        mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
+        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
                 null /* recentsAnimationRunner */);
 
         verify(recentsActivity).ensureActivityConfiguration(anyInt() /* globalChanges */,
@@ -381,7 +381,7 @@
 
         Intent recentsIntent = new Intent();
         recentsIntent.setComponent(recentsComponent);
-        mAtm.startRecentsActivity(recentsIntent, null /* assistDataReceiver */,
+        mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */,
                 mock(IRecentsAnimationRunner.class));
         return recentsAnimation[0];
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index d7eedd9..d0a5644 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -147,13 +147,6 @@
     }
 
     @Override
-    public SurfaceControl.Transaction deferTransactionUntilSurface(SurfaceControl sc,
-            Surface barrierSurface,
-            long frameNumber) {
-        return this;
-    }
-
-    @Override
     public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
             SurfaceControl newParent) {
         return this;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index f151d9c..8e56e5b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -37,7 +37,6 @@
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
 import android.app.PendingIntent;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
@@ -1431,10 +1430,11 @@
         private boolean hasObserverPermission() {
             final int callingUid = Binder.getCallingUid();
             DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+            //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode.
             if (callingUid == Process.SYSTEM_UID
                     || (dpmInternal != null
-                        && dpmInternal.isActiveAdminWithPolicy(callingUid,
-                            DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))) {
+                        && (dpmInternal.isActiveProfileOwner(callingUid)
+                        || dpmInternal.isActiveDeviceOwner(callingUid)))) {
                 // Caller is the system or the profile owner, so proceed.
                 return true;
             }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7b84385..12e56cc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -85,8 +85,6 @@
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
 import android.telephony.ims.ImsMmTelManager;
 import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -94,7 +92,6 @@
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CellNetworkScanResult;
@@ -7384,80 +7381,6 @@
     }
 
     /**
-     * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
-     * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
-     * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
-     * @param callback Listener that will send updates to ImsManager when there are updates to
-     * ImsServiceController.
-     * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
-     * it is unavailable.
-     * @hide
-     */
-    public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
-            IImsServiceFeatureCallback callback) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                return telephony.getMmTelFeatureAndListen(slotIndex, callback);
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
-                    + e.getMessage());
-        }
-        return null;
-    }
-
-    /**
-     * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS
-     * feature for emergency calling or {@link null} if the service is not available. If an
-     * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
-     * listener for feature updates.
-     * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for.
-     * @param callback Listener that will send updates to ImsManager when there are updates to
-     * ImsServiceController.
-     * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if
-     * it is unavailable.
-     * @hide
-     */
-    public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex,
-            IImsServiceFeatureCallback callback) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                return telephony.getRcsFeatureAndListen(slotIndex, callback);
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: "
-                    + e.getMessage());
-        }
-        return null;
-    }
-
-    /**
-     * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through
-     * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or
-     * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}.
-     * @param slotIndex The SIM slot associated with the callback.
-     * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType}
-     *                    associated with the callback.
-     * @param callback The callback to be unregistered.
-     * @hide
-     */
-    public void unregisterImsFeatureCallback(int slotIndex, int featureType,
-            IImsServiceFeatureCallback callback) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null) {
-                telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback);
-            }
-        } catch (RemoteException e) {
-            Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: "
-                    + e.getMessage());
-        }
-    }
-
-    /**
      * @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
      * @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
      * @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index f6c14e6..ee2fce7 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -59,6 +59,7 @@
  * manager.
  */
 public class ImsMmTelManager implements RegistrationManager {
+    private static final String TAG = "ImsMmTelManager";
 
     /**
      * @hide
@@ -809,7 +810,7 @@
         }
 
         try {
-            getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+            iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
                 @Override
                 public void accept(int result) {
                     executor.execute(() -> callback.accept(result == 1));
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index da7311c..8a05bdf 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,6 +16,7 @@
 
 package android.telephony.ims;
 
+import android.annotation.LongDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.Service;
@@ -41,6 +42,11 @@
 import com.android.ims.internal.IImsFeatureStatusCallback;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
  * ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -98,6 +104,32 @@
     private static final String LOG_TAG = "ImsService";
 
     /**
+     * This ImsService supports the capability to place emergency calls over MMTEL.
+     * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
+     * adding other capabilities in a central location, so track this capability here as well.
+     */
+    public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
+
+    /**
+     * @hide
+     */
+    @LongDef(flag = true,
+            prefix = "CAPABILITY_",
+            value = {
+                    CAPABILITY_EMERGENCY_OVER_MMTEL
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsServiceCapability {}
+
+    /**
+     * Used for logging purposes, see {@link #getCapabilitiesString(long)}
+     * @hide
+     */
+    private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
+            put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+        }};
+
+    /**
      * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
      * @hide
      */
@@ -409,4 +441,30 @@
     public ImsRegistrationImplBase getRegistration(int slotId) {
         return new ImsRegistrationImplBase();
     }
+
+    /**
+     * @return A string representation of the ImsService capabilties for logging.
+     * @hide
+     */
+    public static String getCapabilitiesString(@ImsServiceCapability long caps) {
+        StringBuffer result = new StringBuffer();
+        result.append("capabilities={ ");
+        // filter incrementally fills 0s from  left to right. This is used to keep filtering out
+        // more bits in the long until the remaining leftmost bits are all zero.
+        long filter = 0xFFFFFFFFFFFFFFFFL;
+        // position of iterator to potentially print capability.
+        long i = 0;
+        while ((caps & filter) != 0 && i <= 63) {
+            long bitToCheck = (1L << i);
+            if ((caps & bitToCheck) != 0) {
+                result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
+                result.append(" ");
+            }
+            // shift left by one and fill in another 1 on the leftmost bit.
+            filter <<= 1;
+            i++;
+        }
+        result.append("}");
+        return result.toString();
+    }
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 9e46142..e01ea91 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -22,6 +22,8 @@
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 
+import com.android.ims.ImsFeatureContainer;
+import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.IIntegerConsumer;
 
 /**
@@ -50,4 +52,8 @@
     void setUceSettingEnabled(int subId, boolean isEnabled);
     void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
     void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+
+    // Internal commands that should not be made public
+    void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
+    void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
 }
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.aidl b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
new file mode 100644
index 0000000..9706f20
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.ims;
+
+parcelable ImsFeatureContainer;
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
new file mode 100644
index 0000000..b259679
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.java
@@ -0,0 +1,172 @@
+/*
+ * 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.ims;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.ImsFeature;
+
+import java.util.Objects;
+
+/**
+ * Contains an IBinder linking to the appropriate ImsFeature as well as the associated
+ * interfaces.
+ * @hide
+ */
+public final class ImsFeatureContainer implements Parcelable {
+    /**
+     * ImsFeature that is being tracked.
+     */
+    public final IBinder imsFeature;
+
+    /**
+     * IImsConfig interface that should be associated with the ImsFeature.
+     */
+    public final android.telephony.ims.aidl.IImsConfig imsConfig;
+
+    /**
+     * IImsRegistration interface that should be associated with this ImsFeature.
+     */
+    public final IImsRegistration imsRegistration;
+
+    /**
+     * State of the feature that is being tracked.
+     */
+    private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
+
+    /**
+     * Capabilities of this ImsService.
+     */
+    private @ImsService.ImsServiceCapability long mCapabilities;
+    /**
+     * Contains the ImsFeature IBinder as well as the ImsService interfaces associated with
+     * that feature.
+     * @param iFace IBinder connection to the ImsFeature.
+     * @param iConfig IImsConfig interface associated with the ImsFeature.
+     * @param iReg IImsRegistration interface associated with the ImsFeature
+     * @param initialCaps The initial capabilities that the ImsService supports.
+     */
+    public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
+            @NonNull IImsRegistration iReg, long initialCaps) {
+        imsFeature = iFace;
+        imsConfig = iConfig;
+        imsRegistration = iReg;
+        mCapabilities = initialCaps;
+    }
+
+    /**
+     * Create an ImsFeatureContainer from a Parcel.
+     */
+    private ImsFeatureContainer(Parcel in) {
+        imsFeature = in.readStrongBinder();
+        imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
+        imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+        mState = in.readInt();
+        mCapabilities = in.readLong();
+    }
+
+    /**
+     * @return the capabilties that are associated with the ImsService that this ImsFeature
+     * belongs to.
+     */
+    public @ImsService.ImsServiceCapability long getCapabilities() {
+        return mCapabilities;
+    }
+
+    /**
+     * Update the capabilities that are associated with the ImsService that this ImsFeature
+     * belongs to.
+     */
+    public void setCapabilities(@ImsService.ImsServiceCapability long caps) {
+        mCapabilities = caps;
+    }
+
+    /**
+     * @return The state of the ImsFeature.
+     */
+    public @ImsFeature.ImsState int getState() {
+        return mState;
+    }
+
+    /**
+     * Set the state that is associated with the ImsService that this ImsFeature
+     * belongs to.
+     */
+    public void setState(@ImsFeature.ImsState int state) {
+        mState = state;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ImsFeatureContainer that = (ImsFeatureContainer) o;
+        return imsFeature.equals(that.imsFeature) &&
+                imsConfig.equals(that.imsConfig) &&
+                imsRegistration.equals(that.imsRegistration) &&
+                mState == that.getState() &&
+                mCapabilities == that.getCapabilities();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+    }
+
+    @Override
+    public String toString() {
+        return "FeatureContainer{" +
+                "imsFeature=" + imsFeature +
+                ", imsConfig=" + imsConfig +
+                ", imsRegistration=" + imsRegistration +
+                ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
+                ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
+                '}';
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(imsFeature);
+        dest.writeStrongInterface(imsConfig);
+        dest.writeStrongInterface(imsRegistration);
+        dest.writeInt(mState);
+        dest.writeLong(mCapabilities);
+    }
+
+
+    public static final Creator<ImsFeatureContainer> CREATOR = new Creator<ImsFeatureContainer>() {
+        @Override
+        public ImsFeatureContainer createFromParcel(Parcel source) {
+            return new ImsFeatureContainer(source);
+        }
+
+        @Override
+        public ImsFeatureContainer[] newArray(int size) {
+            return new ImsFeatureContainer[size];
+        }
+    };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index 9a9cf53..f5f67bd 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -16,13 +16,18 @@
 
 package com.android.ims.internal;
 
+import com.android.ims.ImsFeatureContainer;
 /**
- *  Interface from ImsResolver to ImsServiceProxy in ImsManager.
- * Callback to ImsManager when a feature changes in the ImsServiceController.
+ *  Interface from ImsResolver to FeatureConnections.
+ * Callback to FeatureConnections when a feature's status changes.
  * {@hide}
  */
 oneway interface IImsServiceFeatureCallback {
-    void imsFeatureCreated(int slotId, int feature);
-    void imsFeatureRemoved(int slotId, int feature);
-    void imsStatusChanged(int slotId, int feature, int status);
+    void imsFeatureCreated(in ImsFeatureContainer feature);
+    // Reason defined in FeatureConnector.UnavailableReason
+    void imsFeatureRemoved(int reason);
+    // Status defined in ImsFeature.ImsState.
+    void imsStatusChanged(int status);
+    //Capabilities defined in ImsService.ImsServiceCapability
+    void updateCapabilities(long capabilities);
 }
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d67384c..53069a1 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -829,22 +829,14 @@
      *  as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
      *  interface.
      */
-    IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
-
-    /**
-     *  Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
-     *  as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback
-     *  interface.
-     */
-    IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+    void registerMmTelFeatureCallback(int slotId, in IImsServiceFeatureCallback callback);
 
     /**
      * Unregister a callback that was previously registered through
-     * {@link #getMmTelFeatureAndListen} or {@link #getRcsFeatureAndListen}. This should always be
-     * called when the callback is no longer being used.
+     * {@link #registerMmTelFeatureCallback}. This should always be called when the callback is no
+     * longer being used.
      */
-    void unregisterImsFeatureCallback(int slotId, int featureType,
-            in IImsServiceFeatureCallback callback);
+    void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
 
     /**
     * Returns the IImsRegistration associated with the slot and feature specified.
diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
index 858358c..8b730af 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -22,7 +22,6 @@
 import android.Manifest;
 import android.Manifest.permission;
 import android.app.AppOpsManager;
-import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -167,13 +166,11 @@
     }
 
     private void setIsDeviceOwner(boolean isOwner) {
-        when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
-                .thenReturn(isOwner);
+        when(mDpmi.isActiveDeviceOwner(TEST_UID)).thenReturn(isOwner);
     }
 
     private void setIsProfileOwner(boolean isOwner) {
-        when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
-                .thenReturn(isOwner);
+        when(mDpmi.isActiveProfileOwner(TEST_UID)).thenReturn(isOwner);
     }
 
     private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 7497869..8862405 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1063,17 +1063,23 @@
  public:
   UsesPermission() = default;
   std::string name;
-  std::string requiredFeature;
-  std::string requiredNotFeature;
+  std::vector<std::string> requiredFeatures;
+  std::vector<std::string> requiredNotFeatures;
   int32_t required = true;
   int32_t maxSdkVersion = -1;
 
   void Extract(xml::Element* element) override {
     name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
-    requiredFeature = GetAttributeStringDefault(
-        FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
-    requiredNotFeature = GetAttributeStringDefault(
-        FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+    std::string feature =
+        GetAttributeStringDefault(FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+    if (!feature.empty()) {
+      requiredFeatures.push_back(feature);
+    }
+    feature = GetAttributeStringDefault(FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+    if (!feature.empty()) {
+      requiredNotFeatures.push_back(feature);
+    }
+
     required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
     maxSdkVersion = GetAttributeIntegerDefault(
         FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
@@ -1090,13 +1096,13 @@
       if (maxSdkVersion >= 0) {
         printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
       }
-      if (!requiredFeature.empty()) {
-        printer->Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
-      }
-      if (!requiredNotFeature.empty()) {
-        printer->Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
-      }
       printer->Print("\n");
+      for (const std::string& requiredFeature : requiredFeatures) {
+        printer->Print(StringPrintf("  required-feature='%s'\n", requiredFeature.data()));
+      }
+      for (const std::string& requiredNotFeature : requiredNotFeatures) {
+        printer->Print(StringPrintf("  required-not-feature='%s'\n", requiredNotFeature.data()));
+      }
       if (required == 0) {
         printer->Print(StringPrintf("optional-permission: name='%s'", name.data()));
         if (maxSdkVersion >= 0) {
@@ -1116,6 +1122,38 @@
   }
 };
 
+/** Represents <required-feature> elements. **/
+class RequiredFeature : public ManifestExtractor::Element {
+ public:
+  RequiredFeature() = default;
+  std::string name;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    auto parent_stack = extractor()->parent_stack();
+    if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+      UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+      uses_permission->requiredFeatures.push_back(name);
+    }
+  }
+};
+
+/** Represents <required-not-feature> elements. **/
+class RequiredNotFeature : public ManifestExtractor::Element {
+ public:
+  RequiredNotFeature() = default;
+  std::string name;
+
+  void Extract(xml::Element* element) override {
+    name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+    auto parent_stack = extractor()->parent_stack();
+    if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+      UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+      uses_permission->requiredNotFeatures.push_back(name);
+    }
+  }
+};
+
 /** Represents <uses-permission-sdk-23> elements. **/
 class UsesPermissionSdk23 : public ManifestExtractor::Element {
  public:
@@ -1845,7 +1883,8 @@
       for (xml::Element* child : element->GetChildElements()) {
         if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
             || child->name == "permission") {
-          auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+          // Inflate the element and its descendants
+          auto permission_element = Visit(child);
           manifest->AddChild(permission_element);
         }
       }
@@ -2237,38 +2276,40 @@
   }
 
   const std::unordered_map<std::string, bool> kTagCheck = {
-    {"action", std::is_base_of<Action, T>::value},
-    {"activity", std::is_base_of<Activity, T>::value},
-    {"application", std::is_base_of<Application, T>::value},
-    {"category", std::is_base_of<Category, T>::value},
-    {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
-    {"feature-group", std::is_base_of<FeatureGroup, T>::value},
-    {"input-type", std::is_base_of<InputType, T>::value},
-    {"intent-filter", std::is_base_of<IntentFilter, T>::value},
-    {"meta-data", std::is_base_of<MetaData, T>::value},
-    {"manifest", std::is_base_of<Manifest, T>::value},
-    {"original-package", std::is_base_of<OriginalPackage, T>::value},
-    {"overlay", std::is_base_of<Overlay, T>::value},
-    {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
-    {"permission", std::is_base_of<Permission, T>::value},
-    {"provider", std::is_base_of<Provider, T>::value},
-    {"receiver", std::is_base_of<Receiver, T>::value},
-    {"screen", std::is_base_of<Screen, T>::value},
-    {"service", std::is_base_of<Service, T>::value},
-    {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
-    {"supports-input", std::is_base_of<SupportsInput, T>::value},
-    {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
-    {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
-    {"uses-feature", std::is_base_of<UsesFeature, T>::value},
-    {"uses-permission", std::is_base_of<UsesPermission, T>::value},
-    {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
-    {"uses-library", std::is_base_of<UsesLibrary, T>::value},
-    {"uses-package", std::is_base_of<UsesPackage, T>::value},
-    {"static-library", std::is_base_of<StaticLibrary, T>::value},
-    {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
-    {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
-    {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
-    {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
+      {"action", std::is_base_of<Action, T>::value},
+      {"activity", std::is_base_of<Activity, T>::value},
+      {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
+      {"application", std::is_base_of<Application, T>::value},
+      {"category", std::is_base_of<Category, T>::value},
+      {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+      {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+      {"input-type", std::is_base_of<InputType, T>::value},
+      {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+      {"meta-data", std::is_base_of<MetaData, T>::value},
+      {"manifest", std::is_base_of<Manifest, T>::value},
+      {"original-package", std::is_base_of<OriginalPackage, T>::value},
+      {"overlay", std::is_base_of<Overlay, T>::value},
+      {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+      {"permission", std::is_base_of<Permission, T>::value},
+      {"provider", std::is_base_of<Provider, T>::value},
+      {"receiver", std::is_base_of<Receiver, T>::value},
+      {"required-feature", std::is_base_of<RequiredFeature, T>::value},
+      {"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
+      {"screen", std::is_base_of<Screen, T>::value},
+      {"service", std::is_base_of<Service, T>::value},
+      {"static-library", std::is_base_of<StaticLibrary, T>::value},
+      {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+      {"supports-input", std::is_base_of<SupportsInput, T>::value},
+      {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+      {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+      {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+      {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+      {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
+      {"uses-package", std::is_base_of<UsesPackage, T>::value},
+      {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+      {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+      {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+      {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
   };
 
   auto check = kTagCheck.find(element->tag());
@@ -2288,39 +2329,41 @@
   const std::unordered_map<std::string,
                            std::function<std::unique_ptr<ManifestExtractor::Element>()>>
       kTagCheck = {
-    {"action", &CreateType<Action>},
-    {"activity", &CreateType<Activity>},
-    {"application", &CreateType<Application>},
-    {"category", &CreateType<Category>},
-    {"compatible-screens", &CreateType<CompatibleScreens>},
-    {"feature-group", &CreateType<FeatureGroup>},
-    {"input-type", &CreateType<InputType>},
-    {"intent-filter",&CreateType<IntentFilter>},
-    {"manifest", &CreateType<Manifest>},
-    {"meta-data", &CreateType<MetaData>},
-    {"original-package", &CreateType<OriginalPackage>},
-    {"overlay", &CreateType<Overlay>},
-    {"package-verifier", &CreateType<PackageVerifier>},
-    {"permission", &CreateType<Permission>},
-    {"provider", &CreateType<Provider>},
-    {"receiver", &CreateType<Receiver>},
-    {"screen", &CreateType<Screen>},
-    {"service", &CreateType<Service>},
-    {"supports-gl-texture", &CreateType<SupportsGlTexture>},
-    {"supports-input", &CreateType<SupportsInput>},
-    {"supports-screens", &CreateType<SupportsScreen>},
-    {"uses-configuration", &CreateType<UsesConfiguarion>},
-    {"uses-feature", &CreateType<UsesFeature>},
-    {"uses-permission", &CreateType<UsesPermission>},
-    {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
-    {"uses-library", &CreateType<UsesLibrary>},
-    {"static-library", &CreateType<StaticLibrary>},
-    {"uses-static-library", &CreateType<UsesStaticLibrary>},
-    {"uses-package", &CreateType<UsesPackage>},
-    {"additional-certificate", &CreateType<AdditionalCertificate>},
-    {"uses-sdk", &CreateType<UsesSdkBadging>},
-    {"uses-native-library", &CreateType<UsesNativeLibrary>},
-  };
+          {"action", &CreateType<Action>},
+          {"activity", &CreateType<Activity>},
+          {"additional-certificate", &CreateType<AdditionalCertificate>},
+          {"application", &CreateType<Application>},
+          {"category", &CreateType<Category>},
+          {"compatible-screens", &CreateType<CompatibleScreens>},
+          {"feature-group", &CreateType<FeatureGroup>},
+          {"input-type", &CreateType<InputType>},
+          {"intent-filter", &CreateType<IntentFilter>},
+          {"manifest", &CreateType<Manifest>},
+          {"meta-data", &CreateType<MetaData>},
+          {"original-package", &CreateType<OriginalPackage>},
+          {"overlay", &CreateType<Overlay>},
+          {"package-verifier", &CreateType<PackageVerifier>},
+          {"permission", &CreateType<Permission>},
+          {"provider", &CreateType<Provider>},
+          {"receiver", &CreateType<Receiver>},
+          {"required-feature", &CreateType<RequiredFeature>},
+          {"required-not-feature", &CreateType<RequiredNotFeature>},
+          {"screen", &CreateType<Screen>},
+          {"service", &CreateType<Service>},
+          {"static-library", &CreateType<StaticLibrary>},
+          {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+          {"supports-input", &CreateType<SupportsInput>},
+          {"supports-screens", &CreateType<SupportsScreen>},
+          {"uses-configuration", &CreateType<UsesConfiguarion>},
+          {"uses-feature", &CreateType<UsesFeature>},
+          {"uses-library", &CreateType<UsesLibrary>},
+          {"uses-native-library", &CreateType<UsesNativeLibrary>},
+          {"uses-package", &CreateType<UsesPackage>},
+          {"uses-permission", &CreateType<UsesPermission>},
+          {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+          {"uses-sdk", &CreateType<UsesSdkBadging>},
+          {"uses-static-library", &CreateType<UsesStaticLibrary>},
+      };
 
   // Attempt to map the xml tag to a element inflater
   std::unique_ptr<ManifestExtractor::Element> element;
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index dac21d7..3d8c25e 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -393,6 +393,8 @@
   manifest_action["protected-broadcast"];
   manifest_action["adopt-permissions"];
   manifest_action["uses-permission"];
+  manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty);
+  manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty);
   manifest_action["uses-permission-sdk-23"];
   manifest_action["permission"];
   manifest_action["permission"]["meta-data"] = meta_data_action;