Merge "[Media ML] Let MCS manage MediaSession2" into sc-dev
diff --git a/Android.bp b/Android.bp
index e4c5c37..908280e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -371,7 +371,6 @@
     srcs: [
         // Java/AIDL sources under frameworks/base
         ":framework-blobstore-sources",
-        ":framework-connectivity-sources", // framework-connectivity is not yet a module
         ":framework-core-sources",
         ":framework-drm-sources",
         ":framework-graphics-nonupdatable-sources",
@@ -437,6 +436,7 @@
     name: "framework-updatable-sources",
     srcs: [
         ":framework-appsearch-sources",
+        ":framework-connectivity-sources",
         ":framework-graphics-srcs",
         ":framework-mediaprovider-sources",
         ":framework-permission-sources",
@@ -639,6 +639,7 @@
     defaults: ["framework-aidl-export-defaults"],
     srcs: [
         ":framework-non-updatable-sources",
+        ":framework-connectivity-sources",
         "core/java/**/*.logtags",
     ],
     // See comment on framework-atb-backward-compatibility module below
@@ -700,6 +701,8 @@
     apex_available: ["//apex_available:platform"],
     visibility: [
         "//frameworks/base",
+        // TODO: remove when framework-connectivity can build against API
+        "//frameworks/base/packages/Connectivity/framework",
         // TODO(b/147128803) remove the below lines
         "//frameworks/base/apex/appsearch/framework",
         "//frameworks/base/apex/blobstore/framework",
@@ -1452,6 +1455,7 @@
     ],
     libs: [
         "framework-annotations-lib",
+        "framework-connectivity",
         "unsupportedappusage",
     ],
     visibility: [
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 4bd524f..bff222e 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -58,6 +58,9 @@
         local_include_dirs: [
             "apex/media/aidl/stable",
             "media/aidl",
+            // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of
+            // frameworks/base
+            "packages/Connectivity/framework/aidl-export",
             "telephony/java",
         ],
         include_dirs: ["frameworks/av/aidl"],
@@ -310,6 +313,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs",
+        "framework-connectivity.stubs",
         "framework-graphics.stubs",
         "framework-media.stubs",
         "framework-mediaprovider.stubs",
@@ -334,6 +338,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
+        "framework-connectivity.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
@@ -374,6 +379,7 @@
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "framework-appsearch.stubs.system",
+        "framework-connectivity.stubs.system",
         "framework-graphics.stubs.system",
         "framework-media.stubs.system",
         "framework-mediaprovider.stubs.system",
diff --git a/api/Android.bp b/api/Android.bp
index 15c1dfc..1d4698e 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -40,6 +40,7 @@
         ":art.module.public.api{.public.api.txt}",
         ":conscrypt.module.public.api{.public.api.txt}",
         ":framework-appsearch{.public.api.txt}",
+        ":framework-connectivity{.public.api.txt}",
         ":framework-graphics{.public.api.txt}",
         ":framework-media{.public.api.txt}",
         ":framework-mediaprovider{.public.api.txt}",
@@ -95,6 +96,7 @@
         ":art.module.public.api{.public.stubs.source}",
         ":conscrypt.module.public.api{.public.stubs.source}",
         ":framework-appsearch{.public.stubs.source}",
+        ":framework-connectivity{.public.stubs.source}",
         ":framework-graphics{.public.stubs.source}",
         ":framework-media{.public.stubs.source}",
         ":framework-mediaprovider{.public.stubs.source}",
@@ -120,6 +122,7 @@
         ":art.module.public.api{.public.removed-api.txt}",
         ":conscrypt.module.public.api{.public.removed-api.txt}",
         ":framework-appsearch{.public.removed-api.txt}",
+        ":framework-connectivity{.public.removed-api.txt}",
         ":framework-graphics{.public.removed-api.txt}",
         ":framework-media{.public.removed-api.txt}",
         ":framework-mediaprovider{.public.removed-api.txt}",
@@ -155,6 +158,7 @@
     srcs: [
         ":android.net.ipsec.ike{.system.api.txt}",
         ":framework-appsearch{.system.api.txt}",
+        ":framework-connectivity{.system.api.txt}",
         ":framework-graphics{.system.api.txt}",
         ":framework-media{.system.api.txt}",
         ":framework-mediaprovider{.system.api.txt}",
@@ -208,6 +212,7 @@
     srcs: [
         ":android.net.ipsec.ike{.system.removed-api.txt}",
         ":framework-appsearch{.system.removed-api.txt}",
+        ":framework-connectivity{.system.removed-api.txt}",
         ":framework-graphics{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
         ":framework-mediaprovider{.system.removed-api.txt}",
@@ -243,6 +248,7 @@
     srcs: [
         ":android.net.ipsec.ike{.module-lib.api.txt}",
         ":framework-appsearch{.module-lib.api.txt}",
+        ":framework-connectivity{.module-lib.api.txt}",
         ":framework-graphics{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
         ":framework-mediaprovider{.module-lib.api.txt}",
@@ -298,6 +304,7 @@
     srcs: [
         ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
         ":framework-appsearch{.module-lib.removed-api.txt}",
+        ":framework-connectivity{.module-lib.removed-api.txt}",
         ":framework-graphics{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
         ":framework-mediaprovider{.module-lib.removed-api.txt}",
diff --git a/core/api/current.txt b/core/api/current.txt
index 6bff005..481a712 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -21,7 +21,6 @@
     field public static final String ACTIVITY_RECOGNITION = "android.permission.ACTIVITY_RECOGNITION";
     field public static final String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
     field public static final String ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS";
-    field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
@@ -134,7 +133,6 @@
     field public static final String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
     field public static final String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
     field public static final String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
-    field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final String REQUEST_COMPANION_PROFILE_WATCH = "android.permission.REQUEST_COMPANION_PROFILE_WATCH";
     field public static final String REQUEST_COMPANION_RUN_IN_BACKGROUND = "android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND";
@@ -12563,7 +12561,6 @@
     field public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
     field public static final String FEATURE_WIFI_PASSPOINT = "android.hardware.wifi.passpoint";
     field public static final String FEATURE_WIFI_RTT = "android.hardware.wifi.rtt";
-    field public static final int FLAG_PERMISSION_ALLOWLIST_ROLE = 8; // 0x8
     field public static final int FLAG_PERMISSION_WHITELIST_INSTALLER = 2; // 0x2
     field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1
     field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4
@@ -12702,7 +12699,6 @@
     field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4
     field public static final int FLAG_IMMUTABLY_RESTRICTED = 16; // 0x10
     field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000
-    field public static final int FLAG_INSTALLER_EXEMPT_IGNORED = 32; // 0x20
     field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8
     field public static final int PROTECTION_DANGEROUS = 1; // 0x1
     field public static final int PROTECTION_FLAG_APPOP = 64; // 0x40
@@ -12902,6 +12898,7 @@
     method public void reportShortcutUsed(String);
     method public boolean requestPinShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.content.IntentSender);
     method public boolean setDynamicShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
+    method public void updateShortcutVisibility(@NonNull String, @Nullable byte[], boolean);
     method public boolean updateShortcuts(@NonNull java.util.List<android.content.pm.ShortcutInfo>);
     field public static final int FLAG_MATCH_CACHED = 8; // 0x8
     field public static final int FLAG_MATCH_DYNAMIC = 2; // 0x2
@@ -19293,8 +19290,8 @@
     method @NonNull public android.location.GnssAntennaInfo.Builder setSignalGainCorrections(@Nullable android.location.GnssAntennaInfo.SphericalCorrections);
   }
 
-  @Deprecated public static interface GnssAntennaInfo.Listener {
-    method @Deprecated public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
+  public static interface GnssAntennaInfo.Listener {
+    method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
   }
 
   public static final class GnssAntennaInfo.PhaseCenterOffset implements android.os.Parcelable {
@@ -19683,7 +19680,7 @@
     method public boolean hasProvider(@NonNull String);
     method public boolean isLocationEnabled();
     method public boolean isProviderEnabled(@NonNull String);
-    method @Deprecated public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener);
+    method public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
@@ -19720,13 +19717,11 @@
     method public void setTestProviderEnabled(@NonNull String, boolean);
     method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location);
     method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long);
-    method @Deprecated public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener);
+    method public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener);
     method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
-    field public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED = "android.location.action.GNSS_ANTENNA_INFOS_CHANGED";
     field public static final String ACTION_GNSS_CAPABILITIES_CHANGED = "android.location.action.GNSS_CAPABILITIES_CHANGED";
-    field public static final String EXTRA_GNSS_ANTENNA_INFOS = "android.location.extra.GNSS_ANTENNA_INFOS";
     field public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES";
     field public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
     field public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED";
@@ -19869,7 +19864,6 @@
     method public int getFlags();
     method public int getUsage();
     method public int getVolumeControlStream();
-    method @NonNull public static String usageToString(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ALLOW_CAPTURE_BY_ALL = 1; // 0x1
     field public static final int ALLOW_CAPTURE_BY_NONE = 3; // 0x3
@@ -21302,8 +21296,8 @@
   public static final class MediaCodecInfo.AudioCapabilities {
     method public android.util.Range<java.lang.Integer> getBitrateRange();
     method @NonNull public android.util.Range<java.lang.Integer>[] getInputChannelCountRanges();
-    method public int getMaxInputChannelCount();
-    method public int getMinInputChannelCount();
+    method @IntRange(from=1, to=255) public int getMaxInputChannelCount();
+    method @IntRange(from=1, to=255) public int getMinInputChannelCount();
     method public android.util.Range<java.lang.Integer>[] getSupportedSampleRateRanges();
     method public int[] getSupportedSampleRates();
     method public boolean isSampleRateSupported(int);
@@ -25827,161 +25821,6 @@
 
 package android.net {
 
-  public class CaptivePortal implements android.os.Parcelable {
-    method public int describeContents();
-    method public void ignoreNetwork();
-    method public void reportCaptivePortalDismissed();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
-  }
-
-  public class ConnectivityDiagnosticsManager {
-    method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
-    method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
-  }
-
-  public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
-    ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
-    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
-    method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
-    method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
-  }
-
-  public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
-    method public int describeContents();
-    method @NonNull public android.os.PersistableBundle getAdditionalInfo();
-    method @NonNull public android.net.LinkProperties getLinkProperties();
-    method @NonNull public android.net.Network getNetwork();
-    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
-    method public long getReportTimestamp();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
-    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
-    field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
-    field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
-    field public static final int NETWORK_PROBE_DNS = 4; // 0x4
-    field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
-    field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
-    field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
-    field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
-    field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
-    field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
-    field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
-    field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
-  }
-
-  public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
-    method public int describeContents();
-    method public int getDetectionMethod();
-    method @NonNull public android.net.LinkProperties getLinkProperties();
-    method @NonNull public android.net.Network getNetwork();
-    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
-    method public long getReportTimestamp();
-    method @NonNull public android.os.PersistableBundle getStallDetails();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
-    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
-    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
-    field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
-    field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
-    field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
-  }
-
-  public class ConnectivityManager {
-    method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public boolean bindProcessToNetwork(@Nullable android.net.Network);
-    method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
-    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
-    method @Deprecated public boolean getBackgroundDataSetting();
-    method @Nullable public android.net.Network getBoundNetworkForProcess();
-    method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
-    method @Nullable public android.net.ProxyInfo getDefaultProxy();
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network);
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network);
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int);
-    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference();
-    method @Nullable public byte[] getNetworkWatchlistConfigHash();
-    method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork();
-    method public int getRestrictBackgroundStatus();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
-    method public boolean isDefaultNetworkActive();
-    method @Deprecated public static boolean isNetworkTypeValid(int);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
-    method public void releaseNetworkRequest(@NonNull android.app.PendingIntent);
-    method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener);
-    method @Deprecated public void reportBadNetwork(@Nullable android.net.Network);
-    method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean);
-    method public boolean requestBandwidthUpdate(@NonNull android.net.Network);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int);
-    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
-    method @Deprecated public void setNetworkPreference(int);
-    method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network);
-    method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
-    method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent);
-    field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
-    field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
-    field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
-    field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
-    field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
-    field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
-    field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
-    field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo";
-    field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover";
-    field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
-    field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
-    field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
-    field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
-    field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
-    field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
-    field public static final String EXTRA_REASON = "reason";
-    field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
-    field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
-    field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2
-    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
-    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
-    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
-    field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7
-    field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8
-    field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9
-    field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0
-    field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4
-    field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
-    field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2
-    field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3
-    field @Deprecated public static final int TYPE_VPN = 17; // 0x11
-    field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
-    field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6
-  }
-
-  public static class ConnectivityManager.NetworkCallback {
-    ctor public ConnectivityManager.NetworkCallback();
-    method public void onAvailable(@NonNull android.net.Network);
-    method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
-    method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
-    method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties);
-    method public void onLosing(@NonNull android.net.Network, int);
-    method public void onLost(@NonNull android.net.Network);
-    method public void onUnavailable();
-  }
-
-  public static interface ConnectivityManager.OnNetworkActiveListener {
-    method public void onNetworkActive();
-  }
-
   public class Credentials {
     ctor public Credentials(int, int, int);
     method public int getGid();
@@ -25989,46 +25828,6 @@
     method public int getUid();
   }
 
-  public class DhcpInfo implements android.os.Parcelable {
-    ctor public DhcpInfo();
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
-    field public int dns1;
-    field public int dns2;
-    field public int gateway;
-    field public int ipAddress;
-    field public int leaseDuration;
-    field public int netmask;
-    field public int serverAddress;
-  }
-
-  public final class DnsResolver {
-    method @NonNull public static android.net.DnsResolver getInstance();
-    method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
-    method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
-    method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
-    method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
-    field public static final int CLASS_IN = 1; // 0x1
-    field public static final int ERROR_PARSE = 0; // 0x0
-    field public static final int ERROR_SYSTEM = 1; // 0x1
-    field public static final int FLAG_EMPTY = 0; // 0x0
-    field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
-    field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
-    field public static final int FLAG_NO_RETRY = 1; // 0x1
-    field public static final int TYPE_A = 1; // 0x1
-    field public static final int TYPE_AAAA = 28; // 0x1c
-  }
-
-  public static interface DnsResolver.Callback<T> {
-    method public void onAnswer(@NonNull T, int);
-    method public void onError(@NonNull android.net.DnsResolver.DnsException);
-  }
-
-  public static class DnsResolver.DnsException extends java.lang.Exception {
-    field public final int code;
-  }
-
   public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile {
     method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms();
     method public int getMaxMtu();
@@ -26058,21 +25857,6 @@
     method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
   }
 
-  public class InetAddresses {
-    method public static boolean isNumericAddress(@NonNull String);
-    method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
-  }
-
-  public final class IpPrefix implements android.os.Parcelable {
-    method public boolean contains(@NonNull java.net.InetAddress);
-    method public int describeContents();
-    method @NonNull public java.net.InetAddress getAddress();
-    method @IntRange(from=0, to=128) public int getPrefixLength();
-    method @NonNull public byte[] getRawAddress();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
-  }
-
   public final class IpSecAlgorithm implements android.os.Parcelable {
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]);
     ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int);
@@ -26141,45 +25925,6 @@
     method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int);
   }
 
-  public class LinkAddress implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.net.InetAddress getAddress();
-    method public int getFlags();
-    method @IntRange(from=0, to=128) public int getPrefixLength();
-    method public int getScope();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
-  }
-
-  public final class LinkProperties implements android.os.Parcelable {
-    ctor public LinkProperties();
-    method public boolean addRoute(@NonNull android.net.RouteInfo);
-    method public void clear();
-    method public int describeContents();
-    method @Nullable public java.net.Inet4Address getDhcpServerAddress();
-    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
-    method @Nullable public String getDomains();
-    method @Nullable public android.net.ProxyInfo getHttpProxy();
-    method @Nullable public String getInterfaceName();
-    method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
-    method public int getMtu();
-    method @Nullable public android.net.IpPrefix getNat64Prefix();
-    method @Nullable public String getPrivateDnsServerName();
-    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
-    method public boolean isPrivateDnsActive();
-    method public boolean isWakeOnLanSupported();
-    method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
-    method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
-    method public void setDomains(@Nullable String);
-    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
-    method public void setInterfaceName(@Nullable String);
-    method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
-    method public void setMtu(int);
-    method public void setNat64Prefix(@Nullable android.net.IpPrefix);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
-  }
-
   public class LocalServerSocket implements java.io.Closeable {
     ctor public LocalServerSocket(String) throws java.io.IOException;
     ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
@@ -26235,24 +25980,6 @@
     enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED;
   }
 
-  public final class MacAddress implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
-    method @NonNull public static android.net.MacAddress fromString(@NonNull String);
-    method public int getAddressType();
-    method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
-    method public boolean isLocallyAssigned();
-    method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
-    method @NonNull public byte[] toByteArray();
-    method @NonNull public String toOuiString();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.net.MacAddress BROADCAST_ADDRESS;
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
-    field public static final int TYPE_BROADCAST = 3; // 0x3
-    field public static final int TYPE_MULTICAST = 2; // 0x2
-    field public static final int TYPE_UNICAST = 1; // 0x1
-  }
-
   public class MailTo {
     method public String getBody();
     method public String getCc();
@@ -26264,139 +25991,6 @@
     field public static final String MAILTO_SCHEME = "mailto:";
   }
 
-  public class Network implements android.os.Parcelable {
-    method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
-    method public void bindSocket(java.net.Socket) throws java.io.IOException;
-    method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
-    method public int describeContents();
-    method public static android.net.Network fromNetworkHandle(long);
-    method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException;
-    method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException;
-    method public long getNetworkHandle();
-    method public javax.net.SocketFactory getSocketFactory();
-    method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
-    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities();
-    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
-    method public int describeContents();
-    method public int getLinkDownstreamBandwidthKbps();
-    method public int getLinkUpstreamBandwidthKbps();
-    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
-    method public int getOwnerUid();
-    method public int getSignalStrength();
-    method @Nullable public android.net.TransportInfo getTransportInfo();
-    method public boolean hasCapability(int);
-    method public boolean hasTransport(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
-    field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
-    field public static final int NET_CAPABILITY_CBS = 5; // 0x5
-    field public static final int NET_CAPABILITY_DUN = 2; // 0x2
-    field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
-    field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
-    field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
-    field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
-    field public static final int NET_CAPABILITY_IA = 7; // 0x7
-    field public static final int NET_CAPABILITY_IMS = 4; // 0x4
-    field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
-    field public static final int NET_CAPABILITY_MCX = 23; // 0x17
-    field public static final int NET_CAPABILITY_MMS = 0; // 0x0
-    field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
-    field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
-    field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
-    field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
-    field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
-    field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
-    field public static final int NET_CAPABILITY_RCS = 8; // 0x8
-    field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
-    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
-    field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
-    field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
-    field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
-    field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
-    field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
-    field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
-    field public static final int TRANSPORT_CELLULAR = 0; // 0x0
-    field public static final int TRANSPORT_ETHERNET = 3; // 0x3
-    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
-    field public static final int TRANSPORT_VPN = 4; // 0x4
-    field public static final int TRANSPORT_WIFI = 1; // 0x1
-    field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
-  }
-
-  @Deprecated public class NetworkInfo implements android.os.Parcelable {
-    ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String);
-    method @Deprecated public int describeContents();
-    method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState();
-    method @Deprecated public String getExtraInfo();
-    method @Deprecated public String getReason();
-    method @Deprecated public android.net.NetworkInfo.State getState();
-    method @Deprecated public int getSubtype();
-    method @Deprecated public String getSubtypeName();
-    method @Deprecated public int getType();
-    method @Deprecated public String getTypeName();
-    method @Deprecated public boolean isAvailable();
-    method @Deprecated public boolean isConnected();
-    method @Deprecated public boolean isConnectedOrConnecting();
-    method @Deprecated public boolean isFailover();
-    method @Deprecated public boolean isRoaming();
-    method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String);
-    method @Deprecated public void writeToParcel(android.os.Parcel, int);
-    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
-  }
-
-  @Deprecated public enum NetworkInfo.DetailedState {
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
-  }
-
-  @Deprecated public enum NetworkInfo.State {
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED;
-    enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN;
-  }
-
-  public class NetworkRequest implements android.os.Parcelable {
-    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
-    method public int describeContents();
-    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
-    method public boolean hasCapability(int);
-    method public boolean hasTransport(int);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
-  }
-
-  public static class NetworkRequest.Builder {
-    ctor public NetworkRequest.Builder();
-    method public android.net.NetworkRequest.Builder addCapability(int);
-    method public android.net.NetworkRequest.Builder addTransportType(int);
-    method public android.net.NetworkRequest build();
-    method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
-    method public android.net.NetworkRequest.Builder removeCapability(int);
-    method public android.net.NetworkRequest.Builder removeTransportType(int);
-    method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
-    method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
-  }
-
   public abstract class NetworkSpecifier {
     ctor public NetworkSpecifier();
   }
@@ -26413,44 +26007,6 @@
     field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
   }
 
-  public final class Proxy {
-    ctor public Proxy();
-    method @Deprecated public static String getDefaultHost();
-    method @Deprecated public static int getDefaultPort();
-    method @Deprecated public static String getHost(android.content.Context);
-    method @Deprecated public static int getPort(android.content.Context);
-    field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
-    field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
-  }
-
-  public class ProxyInfo implements android.os.Parcelable {
-    ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
-    method public static android.net.ProxyInfo buildDirectProxy(String, int);
-    method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>);
-    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
-    method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int);
-    method public int describeContents();
-    method public String[] getExclusionList();
-    method public String getHost();
-    method public android.net.Uri getPacFileUrl();
-    method public int getPort();
-    method public boolean isValid();
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR;
-  }
-
-  public final class RouteInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.net.IpPrefix getDestination();
-    method @Nullable public java.net.InetAddress getGateway();
-    method @Nullable public String getInterface();
-    method public boolean hasGateway();
-    method public boolean isDefaultRoute();
-    method public boolean matches(java.net.InetAddress);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
-  }
-
   @Deprecated public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
     ctor @Deprecated public SSLCertificateSocketFactory(int);
     method @Deprecated public java.net.Socket createSocket(java.net.Socket, String, int, boolean) throws java.io.IOException;
@@ -26476,30 +26032,6 @@
     ctor public SSLSessionCache(android.content.Context);
   }
 
-  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
-    method public final void close();
-    method public final void start(@IntRange(from=0xa, to=0xe10) int);
-    method public final void stop();
-    field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
-    field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0
-    field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
-    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
-    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
-    field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
-    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
-    field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
-    field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
-    field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2
-  }
-
-  public static class SocketKeepalive.Callback {
-    ctor public SocketKeepalive.Callback();
-    method public void onDataReceived();
-    method public void onError(int);
-    method public void onStarted();
-    method public void onStopped();
-  }
-
   public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     method public int describeContents();
     method public int getSubscriptionId();
@@ -26557,9 +26089,6 @@
     field public static final int UNSUPPORTED = -1; // 0xffffffff
   }
 
-  public interface TransportInfo {
-  }
-
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method public abstract android.net.Uri.Builder buildUpon();
     method public int compareTo(android.net.Uri);
@@ -31932,10 +31461,10 @@
     method public static android.content.Intent createUserCreationIntent(@Nullable String, @Nullable String, @Nullable String, @Nullable android.os.PersistableBundle);
     method @WorkerThread public android.os.Bundle getApplicationRestrictions(String);
     method public long getSerialNumberForUser(android.os.UserHandle);
-    method @RequiresPermission("android.permission.MANAGE_USERS") public int getUserCount();
+    method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public int getUserCount();
     method public long getUserCreationTime(android.os.UserHandle);
     method public android.os.UserHandle getUserForSerialNumber(long);
-    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional=true) public String getUserName();
+    method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
@@ -40257,7 +39786,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean handleMmi(String, android.telecom.PhoneAccountHandle);
-    method public boolean hasCompanionInCallServiceAccess();
+    method public boolean hasManageOngoingCallsPermission();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInCall();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isInManagedCall();
     method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
@@ -40909,7 +40438,7 @@
   }
 
   public static final class CarrierConfigManager.ImsServiceEntitlement {
-    field public static final String KEY_AES_URL_STRING = "imsserviceentitlement.aes_url_string";
+    field public static final String KEY_ENTITLEMENT_SERVER_URL_STRING = "imsserviceentitlement.entitlement_server_url_string";
     field public static final String KEY_PREFIX = "imsserviceentitlement.";
   }
 
@@ -42137,7 +41666,6 @@
 
   public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public android.os.IBinder getLiveToken();
     method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos();
     method public boolean isReportingRequestedWhileIdle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -48038,6 +47566,32 @@
     method public void onScaleEnd(android.view.ScaleGestureDetector);
   }
 
+  @UiThread public interface ScrollCaptureCallback {
+    method public void onScrollCaptureEnd(@NonNull Runnable);
+    method public void onScrollCaptureImageRequest(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull android.graphics.Rect, @NonNull java.util.function.Consumer<android.graphics.Rect>);
+    method public void onScrollCaptureSearch(@NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.graphics.Rect>);
+    method public void onScrollCaptureStart(@NonNull android.view.ScrollCaptureSession, @NonNull android.os.CancellationSignal, @NonNull Runnable);
+  }
+
+  public class ScrollCaptureSession {
+    ctor public ScrollCaptureSession(@NonNull android.view.Surface, @NonNull android.graphics.Rect, @NonNull android.graphics.Point);
+    method @NonNull public android.graphics.Point getPositionInWindow();
+    method @NonNull public android.graphics.Rect getScrollBounds();
+    method @NonNull public android.view.Surface getSurface();
+  }
+
+  public final class ScrollCaptureTarget {
+    ctor public ScrollCaptureTarget(@NonNull android.view.View, @NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull android.view.ScrollCaptureCallback);
+    method @NonNull public android.view.ScrollCaptureCallback getCallback();
+    method @NonNull public android.view.View getContainingView();
+    method public int getHint();
+    method @NonNull public android.graphics.Rect getLocalVisibleRect();
+    method @NonNull public android.graphics.Point getPositionInWindow();
+    method @Nullable public android.graphics.Rect getScrollBounds();
+    method public void setScrollBounds(@Nullable android.graphics.Rect);
+    method @UiThread public void updatePositionInWindow();
+  }
+
   public class SearchEvent {
     ctor public SearchEvent(android.view.InputDevice);
     method public android.view.InputDevice getInputDevice();
@@ -48364,6 +47918,7 @@
     method public void dispatchProvideStructure(android.view.ViewStructure);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
+    method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
     method protected void dispatchSetActivated(boolean);
     method protected void dispatchSetPressed(boolean);
     method protected void dispatchSetSelected(boolean);
@@ -48523,6 +48078,7 @@
     method public int getScrollBarFadeDuration();
     method public int getScrollBarSize();
     method public int getScrollBarStyle();
+    method public int getScrollCaptureHint();
     method public int getScrollIndicators();
     method public final int getScrollX();
     method public final int getScrollY();
@@ -48691,6 +48247,7 @@
     method public void onRtlPropertiesChanged(int);
     method @CallSuper @Nullable protected android.os.Parcelable onSaveInstanceState();
     method public void onScreenStateChanged(int);
+    method public void onScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
     method protected void onScrollChanged(int, int, int, int);
     method protected boolean onSetAlpha(int);
     method protected void onSizeChanged(int, int, int, int);
@@ -48873,6 +48430,8 @@
     method public void setScrollBarFadeDuration(int);
     method public void setScrollBarSize(int);
     method public void setScrollBarStyle(int);
+    method public final void setScrollCaptureCallback(@Nullable android.view.ScrollCaptureCallback);
+    method public void setScrollCaptureHint(int);
     method public void setScrollContainer(boolean);
     method public void setScrollIndicators(int);
     method public void setScrollIndicators(int, int);
@@ -49051,6 +48610,10 @@
     field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
     field public static final int SCROLL_AXIS_NONE = 0; // 0x0
     field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_CAPTURE_HINT_AUTO = 0; // 0x0
+    field public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 1; // 0x1
+    field public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 4; // 0x4
+    field public static final int SCROLL_CAPTURE_HINT_INCLUDE = 2; // 0x2
     field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
     field public static final int SCROLL_INDICATOR_END = 32; // 0x20
     field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
@@ -49835,6 +49398,7 @@
     method public abstract boolean performContextMenuIdentifierAction(int, int);
     method public abstract boolean performPanelIdentifierAction(int, int, int);
     method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+    method public void registerScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback);
     method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
     method public boolean requestFeature(int);
     method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
@@ -49914,6 +49478,7 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
+    method public void unregisterScrollCaptureCallback(@NonNull android.view.ScrollCaptureCallback);
     field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
     field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
     field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index dd9582f..e6f0e48 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -163,67 +163,14 @@
 
 package android.net {
 
-  public final class ConnectivityFrameworkInitializer {
-    method public static void registerServiceWrappers();
-  }
-
-  public class ConnectivityManager {
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
-  }
-
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public int getResourceId();
   }
 
-  public final class NetworkAgentConfig implements android.os.Parcelable {
-    method @Nullable public String getSubscriberId();
-  }
-
-  public static final class NetworkAgentConfig.Builder {
-    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    field public static final int TRANSPORT_TEST = 7; // 0x7
-  }
-
   public class NetworkWatchlistManager {
     method @Nullable public byte[] getWatchlistConfigHash();
   }
 
-  public final class Proxy {
-    method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
-  }
-
-  public final class TcpRepairWindow {
-    ctor public TcpRepairWindow(int, int, int, int, int, int);
-    field public final int maxWindow;
-    field public final int rcvWnd;
-    field public final int rcvWndScale;
-    field public final int rcvWup;
-    field public final int sndWl1;
-    field public final int sndWnd;
-  }
-
-  public final class TestNetworkInterface implements android.os.Parcelable {
-    ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
-    method public int describeContents();
-    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
-    method @NonNull public String getInterfaceName();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
-  }
-
-  public class TestNetworkManager {
-    method @NonNull public android.net.TestNetworkInterface createTapInterface();
-    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
-    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
-    method public void teardownTestNetwork(@NonNull android.net.Network);
-    field public static final String TEST_TAP_PREFIX = "testtap";
-  }
-
   public final class UnderlyingNetworkInfo implements android.os.Parcelable {
     ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
@@ -234,14 +181,6 @@
     field @NonNull public final java.util.List<java.lang.String> underlyingIfaces;
   }
 
-  public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
-    ctor public VpnTransportInfo(int);
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
-    field public final int type;
-  }
-
 }
 
 package android.os {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 990388a..a99178d 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -241,12 +241,6 @@
 
 package android.net {
 
-  public class ConnectivityManager {
-    method @Deprecated public boolean requestRouteToHost(int, int);
-    method @Deprecated public int startUsingNetworkFeature(int, String);
-    method @Deprecated public int stopUsingNetworkFeature(int, String);
-  }
-
   @Deprecated public class NetworkBadging {
     method @NonNull public static android.graphics.drawable.Drawable getWifiIcon(@IntRange(from=0, to=4) int, int, @Nullable android.content.res.Resources.Theme);
     field public static final int BADGING_4K = 30; // 0x1e
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c62b808..c3d5d5a 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -27,6 +27,7 @@
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+    field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BACKUP = "android.permission.BACKUP";
     field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
     field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
@@ -215,6 +216,7 @@
     field public static final String RECEIVE_DEVICE_CUSTOMIZATION_READY = "android.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY";
     field public static final String RECEIVE_EMERGENCY_BROADCAST = "android.permission.RECEIVE_EMERGENCY_BROADCAST";
     field public static final String RECEIVE_WIFI_CREDENTIAL_CHANGE = "android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE";
+    field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String RECOVERY = "android.permission.RECOVERY";
     field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
     field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
@@ -2464,10 +2466,10 @@
   }
 
   public static class PackageInstaller.Session implements java.io.Closeable {
-    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
+    method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
-    method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams();
-    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String);
+    method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams();
+    method public void removeFile(int, @NonNull String);
   }
 
   public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2488,7 +2490,7 @@
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
     method @Deprecated public void setAllowDowngrade(boolean);
-    method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
+    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
     method public void setDontKillApp(boolean);
     method public void setEnableRollback(boolean);
     method public void setEnableRollback(boolean, int);
@@ -2570,7 +2572,6 @@
     field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000
     field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
     field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800
-    field public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 262144; // 0x40000
     field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
     field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
     field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
@@ -2756,6 +2757,15 @@
 
 package android.content.pm.verify.domain {
 
+  public final class DomainOwner implements android.os.Parcelable {
+    ctor public DomainOwner(@NonNull String, boolean);
+    method public int describeContents();
+    method @NonNull public String getPackageName();
+    method public boolean isOverrideable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainOwner> CREATOR;
+  }
+
   public final class DomainVerificationInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
@@ -2768,6 +2778,7 @@
   public interface DomainVerificationManager {
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION}) public android.content.pm.verify.domain.DomainVerificationInfo getDomainVerificationInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public android.content.pm.verify.domain.DomainVerificationUserSelection getDomainVerificationUserSelection(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION) public java.util.List<android.content.pm.verify.domain.DomainOwner> getOwnersForDomain(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT) public java.util.List<java.lang.String> getValidVerificationPackageNames();
     method public static boolean isStateModifiable(int);
     method public static boolean isStateVerified(int);
@@ -2789,13 +2800,16 @@
 
   public final class DomainVerificationUserSelection implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public java.util.Map<java.lang.String,java.lang.Boolean> getHostToUserSelectionMap();
+    method @NonNull public java.util.Map<java.lang.String,java.lang.Integer> getHostToStateMap();
     method @NonNull public java.util.UUID getIdentifier();
     method @NonNull public String getPackageName();
     method @NonNull public android.os.UserHandle getUser();
     method @NonNull public boolean isLinkHandlingAllowed();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.domain.DomainVerificationUserSelection> CREATOR;
+    field public static final int DOMAIN_STATE_NONE = 0; // 0x0
+    field public static final int DOMAIN_STATE_SELECTED = 1; // 0x1
+    field public static final int DOMAIN_STATE_VERIFIED = 2; // 0x2
   }
 
 }
@@ -7084,102 +7098,6 @@
 
 package android.net {
 
-  public class CaptivePortal implements android.os.Parcelable {
-    method public void logEvent(int, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
-    method public void useNetwork();
-    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
-    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
-    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
-    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
-  }
-
-  public final class CaptivePortalData implements android.os.Parcelable {
-    method public int describeContents();
-    method public long getByteLimit();
-    method public long getExpiryTimeMillis();
-    method public long getRefreshTimeMillis();
-    method @Nullable public android.net.Uri getUserPortalUrl();
-    method public int getUserPortalUrlSource();
-    method @Nullable public String getVenueFriendlyName();
-    method @Nullable public android.net.Uri getVenueInfoUrl();
-    method public int getVenueInfoUrlSource();
-    method public boolean isCaptive();
-    method public boolean isSessionExtendable();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
-    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
-  }
-
-  public static class CaptivePortalData.Builder {
-    ctor public CaptivePortalData.Builder();
-    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
-    method @NonNull public android.net.CaptivePortalData build();
-    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
-    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
-    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
-    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
-    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
-  }
-
-  public class ConnectivityManager {
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
-    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
-    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
-    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
-    method public void unregisterQosCallback(@NonNull android.net.QosCallback);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
-    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
-    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
-    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
-    field public static final int TETHERING_USB = 1; // 0x1
-    field public static final int TETHERING_WIFI = 0; // 0x0
-    field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
-    field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
-    field public static final int TYPE_NONE = -1; // 0xffffffff
-    field @Deprecated public static final int TYPE_PROXY = 16; // 0x10
-    field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
-  }
-
-  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
-    method public void onComplete();
-  }
-
-  @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
-    ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
-    method @Deprecated public void onTetheringFailed();
-    method @Deprecated public void onTetheringStarted();
-  }
-
-  @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
-    method @Deprecated public void onTetheringEntitlementResult(int);
-  }
-
-  @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
-    ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
-    method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
-  }
-
   public class DnsResolverServiceManager {
     method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public static android.os.IBinder getService(@NonNull android.content.Context);
   }
@@ -7197,48 +7115,6 @@
     method public void release();
   }
 
-  public final class InvalidPacketException extends java.lang.Exception {
-    ctor public InvalidPacketException(int);
-    method public int getError();
-    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
-    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
-    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
-  }
-
-  public final class IpConfiguration implements android.os.Parcelable {
-    ctor public IpConfiguration();
-    ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
-    method public int describeContents();
-    method @Nullable public android.net.ProxyInfo getHttpProxy();
-    method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
-    method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
-    method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
-    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
-    method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
-    method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
-    method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
-  }
-
-  public enum IpConfiguration.IpAssignment {
-    enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP;
-    enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC;
-    enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED;
-  }
-
-  public enum IpConfiguration.ProxySettings {
-    enum_constant public static final android.net.IpConfiguration.ProxySettings NONE;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings PAC;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC;
-    enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED;
-  }
-
-  public final class IpPrefix implements android.os.Parcelable {
-    ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public IpPrefix(@NonNull String);
-  }
-
   public final class IpSecManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
@@ -7256,68 +7132,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
   }
 
-  public class KeepalivePacketData {
-    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
-    method @NonNull public java.net.InetAddress getDstAddress();
-    method public int getDstPort();
-    method @NonNull public byte[] getPacket();
-    method @NonNull public java.net.InetAddress getSrcAddress();
-    method public int getSrcPort();
-  }
-
-  public class LinkAddress implements android.os.Parcelable {
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
-    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
-    ctor public LinkAddress(@NonNull String);
-    ctor public LinkAddress(@NonNull String, int, int);
-    method public long getDeprecationTime();
-    method public long getExpirationTime();
-    method public boolean isGlobalPreferred();
-    method public boolean isIpv4();
-    method public boolean isIpv6();
-    method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
-    field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
-    field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
-  }
-
-  public final class LinkProperties implements android.os.Parcelable {
-    ctor public LinkProperties(@Nullable android.net.LinkProperties);
-    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
-    method public boolean addDnsServer(@NonNull java.net.InetAddress);
-    method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
-    method public boolean addPcscfServer(@NonNull java.net.InetAddress);
-    method @NonNull public java.util.List<java.net.InetAddress> getAddresses();
-    method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
-    method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
-    method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
-    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
-    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
-    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
-    method @Nullable public String getTcpBufferSizes();
-    method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
-    method public boolean hasGlobalIpv6Address();
-    method public boolean hasIpv4Address();
-    method public boolean hasIpv4DefaultRoute();
-    method public boolean hasIpv4DnsServer();
-    method public boolean hasIpv6DefaultRoute();
-    method public boolean hasIpv6DnsServer();
-    method public boolean isIpv4Provisioned();
-    method public boolean isIpv6Provisioned();
-    method public boolean isProvisioned();
-    method public boolean isReachable(@NonNull java.net.InetAddress);
-    method public boolean removeDnsServer(@NonNull java.net.InetAddress);
-    method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
-    method public boolean removeRoute(@NonNull android.net.RouteInfo);
-    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
-    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
-    method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
-    method public void setPrivateDnsServerName(@Nullable String);
-    method public void setTcpBufferSizes(@Nullable String);
-    method public void setUsePrivateDns(boolean);
-    method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
-  }
-
   public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public MatchAllNetworkSpecifier();
     method public int describeContents();
@@ -7325,104 +7139,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
   }
 
-  public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
-    ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
-  }
-
-  public class Network implements android.os.Parcelable {
-    ctor public Network(@NonNull android.net.Network);
-    method public int getNetId();
-    method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
-  }
-
-  public abstract class NetworkAgent {
-    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
-    method @Nullable public android.net.Network getNetwork();
-    method public void markConnected();
-    method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
-    method public void onAutomaticReconnectDisabled();
-    method public void onNetworkUnwanted();
-    method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
-    method public void onQosCallbackUnregistered(int);
-    method public void onRemoveKeepalivePacketFilter(int);
-    method public void onSaveAcceptUnvalidated(boolean);
-    method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
-    method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
-    method public void onStopSocketKeepalive(int);
-    method public void onValidationStatus(int, @Nullable android.net.Uri);
-    method @NonNull public android.net.Network register();
-    method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
-    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
-    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
-    method public final void sendQosCallbackError(int, int);
-    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
-    method public final void sendQosSessionLost(int, int);
-    method public final void sendSocketKeepaliveEvent(int, int);
-    method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
-    method public void unregister();
-    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
-    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
-  }
-
-  public final class NetworkAgentConfig implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getLegacyType();
-    method @NonNull public String getLegacyTypeName();
-    method public boolean isExplicitlySelected();
-    method public boolean isPartialConnectivityAcceptable();
-    method public boolean isUnvalidatedConnectivityAcceptable();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
-  }
-
-  public static final class NetworkAgentConfig.Builder {
-    ctor public NetworkAgentConfig.Builder();
-    method @NonNull public android.net.NetworkAgentConfig build();
-    method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
-    method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
-  }
-
-  public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
-    method @NonNull public int[] getAdministratorUids();
-    method @Nullable public String getSsid();
-    method @NonNull public int[] getTransportTypes();
-    method public boolean isPrivateDnsBroken();
-    method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
-    field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
-    field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
-    field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
-    field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
-    field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
-  }
-
-  public static final class NetworkCapabilities.Builder {
-    ctor public NetworkCapabilities.Builder();
-    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
-    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
-    method @NonNull public android.net.NetworkCapabilities build();
-    method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
-    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
-    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
-    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
-  }
-
   public class NetworkKey implements android.os.Parcelable {
     ctor public NetworkKey(android.net.WifiKey);
     method @Nullable public static android.net.NetworkKey createFromScanResult(@NonNull android.net.wifi.ScanResult);
@@ -7434,15 +7150,6 @@
     field public final android.net.WifiKey wifiKey;
   }
 
-  public class NetworkProvider {
-    ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
-    method public int getProviderId();
-    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
-    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
-    field public static final int ID_NONE = -1; // 0xffffffff
-  }
-
   public abstract class NetworkRecommendationProvider {
     ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor);
     method public final android.os.IBinder getBinder();
@@ -7452,15 +7159,6 @@
   public class NetworkReleasedException extends java.lang.Exception {
   }
 
-  public class NetworkRequest implements android.os.Parcelable {
-    method @Nullable public String getRequestorPackageName();
-    method public int getRequestorUid();
-  }
-
-  public static class NetworkRequest.Builder {
-    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
-  }
-
   public class NetworkScoreManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
@@ -7585,16 +7283,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
   }
 
-  public final class RouteInfo implements android.os.Parcelable {
-    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
-    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
-    method public int getMtu();
-    method public int getType();
-    field public static final int RTN_THROW = 9; // 0x9
-    field public static final int RTN_UNICAST = 1; // 0x1
-    field public static final int RTN_UNREACHABLE = 7; // 0x7
-  }
-
   public class RssiCurve implements android.os.Parcelable {
     ctor public RssiCurve(int, int, byte[]);
     ctor public RssiCurve(int, int, byte[], int);
@@ -7626,53 +7314,12 @@
     field public final android.net.RssiCurve rssiCurve;
   }
 
-  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
-    field public static final int SUCCESS = 0; // 0x0
-  }
-
   public class SocketLocalAddressChangedException extends java.lang.Exception {
   }
 
   public class SocketNotBoundException extends java.lang.Exception {
   }
 
-  public final class StaticIpConfiguration implements android.os.Parcelable {
-    ctor public StaticIpConfiguration();
-    ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
-    method public void addDnsServer(@NonNull java.net.InetAddress);
-    method public void clear();
-    method public int describeContents();
-    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
-    method @Nullable public String getDomains();
-    method @Nullable public java.net.InetAddress getGateway();
-    method @Nullable public android.net.LinkAddress getIpAddress();
-    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
-  }
-
-  public static final class StaticIpConfiguration.Builder {
-    ctor public StaticIpConfiguration.Builder();
-    method @NonNull public android.net.StaticIpConfiguration build();
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
-    method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
-  }
-
-  public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
-    ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
-    method public int describeContents();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
-    field public final int ipTos;
-    field public final int ipTtl;
-    field public final int tcpAck;
-    field public final int tcpSeq;
-    field public final int tcpWindow;
-    field public final int tcpWindowScale;
-  }
-
   public class TrafficStats {
     method public static void setThreadStatsTagApp();
     method public static void setThreadStatsTagBackup();
@@ -7685,11 +7332,6 @@
     field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
   }
 
-  public interface TransportInfo {
-    method public default boolean hasLocationSensitiveFields();
-    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
-  }
-
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method @NonNull public String toSafeString();
   }
@@ -7713,23 +7355,6 @@
 
 }
 
-package android.net.apf {
-
-  public final class ApfCapabilities implements android.os.Parcelable {
-    ctor public ApfCapabilities(int, int, int);
-    method public int describeContents();
-    method public static boolean getApfDrop8023Frames();
-    method @NonNull public static int[] getApfEtherTypeBlackList();
-    method public boolean hasDataAccess();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
-    field public final int apfPacketFormat;
-    field public final int apfVersionSupported;
-    field public final int maximumApfProgramSize;
-  }
-
-}
-
 package android.net.metrics {
 
   @Deprecated public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -7924,19 +7549,6 @@
 
 }
 
-package android.net.util {
-
-  public final class SocketUtils {
-    method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
-    method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
-    method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
-    method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
-    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
-  }
-
-}
-
 package android.net.vcn {
 
   public class VcnManager {
@@ -8788,13 +8400,13 @@
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set<java.lang.String>) throws android.os.UserManager.UserOperationException;
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getAllProfiles();
     method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List<android.os.UserHandle> getEnabledProfiles();
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getRestrictedProfileParent();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public long[] getSerialNumbersOfUsers(boolean);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.os.UserHandle> getUserHandles(boolean);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public android.graphics.Bitmap getUserIcon();
     method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle);
@@ -9232,6 +8844,7 @@
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
     field public static final String NAMESPACE_BLOBSTORE = "blobstore";
     field public static final String NAMESPACE_BLUETOOTH = "bluetooth";
+    field public static final String NAMESPACE_CLIPBOARD = "clipboard";
     field public static final String NAMESPACE_CONNECTIVITY = "connectivity";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
     field @Deprecated public static final String NAMESPACE_DEX_BOOT = "dex_boot";
@@ -11159,6 +10772,10 @@
     field public static final String KEY_SUPPORT_CDMA_1X_VOICE_CALLS_BOOL = "support_cdma_1x_voice_calls_bool";
   }
 
+  public static final class CarrierConfigManager.Ims {
+    field public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY = "ims.publish_service_desc_feature_tag_map_override_string_array";
+  }
+
   public static final class CarrierConfigManager.Wifi {
     field public static final String KEY_AVOID_5GHZ_SOFTAP_FOR_LAA_BOOL = "wifi.avoid_5ghz_softap_for_laa_bool";
     field public static final String KEY_AVOID_5GHZ_WIFI_DIRECT_FOR_LAA_BOOL = "wifi.avoid_5ghz_wifi_direct_for_laa_bool";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index de34068..ca261cd 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -5,6 +5,7 @@
     field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+    field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
@@ -30,6 +31,7 @@
     field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+    field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
     field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
@@ -748,6 +750,65 @@
     ctor public ShortcutManager(android.content.Context);
   }
 
+  public class UserInfo implements android.os.Parcelable {
+    ctor public UserInfo(int, String, int);
+    ctor public UserInfo(int, String, String, int);
+    ctor public UserInfo(int, String, String, int, String);
+    ctor @Deprecated public UserInfo();
+    ctor public UserInfo(android.content.pm.UserInfo);
+    method public boolean canHaveProfile();
+    method public int describeContents();
+    method public android.os.UserHandle getUserHandle();
+    method public boolean isAdmin();
+    method public boolean isDemo();
+    method public boolean isEnabled();
+    method public boolean isEphemeral();
+    method public boolean isFull();
+    method public boolean isGuest();
+    method public boolean isInitialized();
+    method public boolean isManagedProfile();
+    method public boolean isPrimary();
+    method public boolean isProfile();
+    method public boolean isQuietModeEnabled();
+    method public boolean isRestricted();
+    method public boolean isSystemOnly();
+    method public static boolean isSystemOnly(int);
+    method public boolean supportsSwitchTo();
+    method public boolean supportsSwitchToByUser();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.UserInfo> CREATOR;
+    field public static final int FLAG_ADMIN = 2; // 0x2
+    field @Deprecated public static final int FLAG_DEMO = 512; // 0x200
+    field public static final int FLAG_DISABLED = 64; // 0x40
+    field public static final int FLAG_EPHEMERAL = 256; // 0x100
+    field public static final int FLAG_FULL = 1024; // 0x400
+    field @Deprecated public static final int FLAG_GUEST = 4; // 0x4
+    field public static final int FLAG_INITIALIZED = 16; // 0x10
+    field @Deprecated public static final int FLAG_MANAGED_PROFILE = 32; // 0x20
+    field public static final int FLAG_PRIMARY = 1; // 0x1
+    field public static final int FLAG_PROFILE = 4096; // 0x1000
+    field public static final int FLAG_QUIET_MODE = 128; // 0x80
+    field @Deprecated public static final int FLAG_RESTRICTED = 8; // 0x8
+    field public static final int FLAG_SYSTEM = 2048; // 0x800
+    field public static final int NO_PROFILE_GROUP_ID = -10000; // 0xffffd8f0
+    field public boolean convertedFromPreCreated;
+    field public long creationTime;
+    field public int flags;
+    field public boolean guestToRemove;
+    field public String iconPath;
+    field public int id;
+    field public String lastLoggedInFingerprint;
+    field public long lastLoggedInTime;
+    field public String name;
+    field public boolean partial;
+    field public boolean preCreated;
+    field public int profileBadge;
+    field public int profileGroupId;
+    field public int restrictedProfileParentId;
+    field public int serialNumber;
+    field public String userType;
+  }
+
 }
 
 package android.content.res {
@@ -1511,8 +1572,15 @@
   }
 
   public class UserManager {
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
+    method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
+    method public static boolean isGuestUserEphemeral();
     method public static boolean isSplitSystemUser();
+    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
   }
 
   public final class VibrationAttributes implements android.os.Parcelable {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 87fb5b1..a536efb 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -468,7 +468,7 @@
 GetterSetterNames: android.location.GnssMeasurement#setCarrierFrequencyHz(float):
     
 GetterSetterNames: android.location.GnssMeasurement#setCodeType(String):
-
+    
 GetterSetterNames: android.location.GnssMeasurement#setCorrelationVectors(java.util.Collection<android.location.CorrelationVector>):
     
 GetterSetterNames: android.location.GnssMeasurement#setFullInterSignalBiasNanos(double):
@@ -480,7 +480,7 @@
 GetterSetterNames: android.location.GnssMeasurement#setSatelliteInterSignalBiasUncertaintyNanos(double):
     
 GetterSetterNames: android.location.GnssMeasurement#setSatellitePvt(android.location.SatellitePvt):
-
+    
 GetterSetterNames: android.location.GnssMeasurement#setSnrInDb(double):
     
 GetterSetterNames: android.location.LocationRequest#isLocationSettingsIgnored():
@@ -953,6 +953,32 @@
     
 MissingNullability: android.content.pm.ShortcutManager#ShortcutManager(android.content.Context) parameter #0:
     
+MissingNullability: android.content.pm.UserInfo#UserInfo(android.content.pm.UserInfo) parameter #0:
+    Missing nullability on parameter `orig` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int) parameter #2:
+    Missing nullability on parameter `iconPath` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #2:
+    Missing nullability on parameter `iconPath` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, String, int, String) parameter #4:
+    Missing nullability on parameter `userType` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#UserInfo(int, String, int) parameter #1:
+    Missing nullability on parameter `name` in method `UserInfo`
+MissingNullability: android.content.pm.UserInfo#getUserHandle():
+    Missing nullability on method `getUserHandle` return
+MissingNullability: android.content.pm.UserInfo#iconPath:
+    Missing nullability on field `iconPath` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#lastLoggedInFingerprint:
+    Missing nullability on field `lastLoggedInFingerprint` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#name:
+    Missing nullability on field `name` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#userType:
+    Missing nullability on field `userType` in class `class android.content.pm.UserInfo`
+MissingNullability: android.content.pm.UserInfo#writeToParcel(android.os.Parcel, int) parameter #0:
+    Missing nullability on parameter `dest` in method `writeToParcel`
 MissingNullability: android.content.res.AssetManager#getOverlayablesToString(String) parameter #0:
     
 MissingNullability: android.content.res.Configuration#windowConfiguration:
@@ -2433,6 +2459,38 @@
     
 MutableBareField: android.content.AutofillOptions#whitelistedActivitiesForAugmentedAutofill:
     
+MutableBareField: android.content.pm.UserInfo#convertedFromPreCreated:
+    Bare field convertedFromPreCreated must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#creationTime:
+    Bare field creationTime must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#flags:
+    Bare field flags must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#guestToRemove:
+    Bare field guestToRemove must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#iconPath:
+    Bare field iconPath must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#id:
+    Bare field id must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#lastLoggedInFingerprint:
+    Bare field lastLoggedInFingerprint must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#lastLoggedInTime:
+    Bare field lastLoggedInTime must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#name:
+    Bare field name must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#partial:
+    Bare field partial must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#preCreated:
+    Bare field preCreated must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#profileBadge:
+    Bare field profileBadge must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#profileGroupId:
+    Bare field profileGroupId must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#restrictedProfileParentId:
+    Bare field restrictedProfileParentId must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#serialNumber:
+    Bare field serialNumber must be marked final, or moved behind accessors if mutable
+MutableBareField: android.content.pm.UserInfo#userType:
+    Bare field userType must be marked final, or moved behind accessors if mutable
 MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#cache:
     
 MutableBareField: android.database.sqlite.SQLiteDebug.DbStats#dbName:
@@ -2548,15 +2606,15 @@
 NoSettingsProvider: android.provider.Settings.Global#USE_OPEN_WIFI_PACKAGE:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_CAPABILITY:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
-
+    
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
     
 NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE:
@@ -2619,6 +2677,10 @@
     
 
 
+NullableCollection: android.os.UserManager#createProfileForUser(String, String, int, int, String[]) parameter #4:
+    Type of parameter disallowedPackages in android.os.UserManager.createProfileForUser(String name, String userType, int flags, int userId, String[] disallowedPackages) is a nullable collection (`java.lang.String[]`); must be non-null
+
+
 OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]):
     
 OnNameExpected: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]):
@@ -2729,6 +2791,8 @@
 
 ParcelNotFinal: android.app.WindowConfiguration:
     
+ParcelNotFinal: android.content.pm.UserInfo:
+    Parcelable classes must be final: android.content.pm.UserInfo is not final
 ParcelNotFinal: android.net.metrics.IpConnectivityLog.Event:
     
 ParcelNotFinal: android.os.IncidentManager.IncidentReport:
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 71f164f..c812e8e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -113,10 +113,6 @@
             IBinder allowlistToken, long duration, @TempAllowListType int type,
             @ReasonCode int reasonCode, @Nullable String reason);
 
-    @Deprecated
-    public abstract void setPendingIntentWhitelistDuration(IIntentSender target,
-            IBinder allowlistToken, long duration, @TempAllowListType int type);
-
     /**
      * Returns the flags set for a {@link PendingIntent}.
      */
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f7304fb..7222e86 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5013,6 +5013,7 @@
             bindNotificationHeader(contentView, p);
             bindLargeIconAndApplyMargin(contentView, p, result);
             boolean showProgress = handleProgressBar(contentView, ex, p);
+            boolean hasSecondLine = showProgress;
             if (p.hasTitle()) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
@@ -5028,11 +5029,27 @@
                 contentView.setTextViewText(textId, processTextSpans(p.text));
                 setTextViewColorSecondary(contentView, textId, p);
                 contentView.setViewVisibility(textId, View.VISIBLE);
+                hasSecondLine = true;
             }
+            setHeaderlessVerticalMargins(contentView, p, hasSecondLine);
 
             return contentView;
         }
 
+        private static void setHeaderlessVerticalMargins(RemoteViews contentView,
+                StandardTemplateParams p, boolean hasSecondLine) {
+            if (!p.mHeaderless) {
+                return;
+            }
+            int marginDimen = hasSecondLine
+                    ? R.dimen.notification_headerless_margin_twoline
+                    : R.dimen.notification_headerless_margin_oneline;
+            contentView.setViewLayoutMarginDimen(R.id.notification_headerless_view_column,
+                    RemoteViews.MARGIN_TOP, marginDimen);
+            contentView.setViewLayoutMarginDimen(R.id.notification_headerless_view_column,
+                    RemoteViews.MARGIN_BOTTOM, marginDimen);
+        }
+
         private CharSequence processTextSpans(CharSequence text) {
             if (hasForegroundColor() || mInNightMode) {
                 return ContrastColorUtil.clearColorSpans(text);
@@ -5494,12 +5511,28 @@
             big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
             big.setTextViewText(R.id.notification_material_reply_text_3, null);
 
-            final boolean snoozeEnabled = mContext.getContentResolver() != null
-                    && (Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
-            int bottomMarginDimen = snoozeEnabled ? 0 : R.dimen.notification_content_margin;
+            // This may get erased by bindSnoozeAction
             big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
-                    RemoteViews.MARGIN_BOTTOM, bottomMarginDimen);
+                    RemoteViews.MARGIN_BOTTOM, R.dimen.notification_content_margin);
+        }
+
+        private void bindSnoozeAction(RemoteViews big, StandardTemplateParams p) {
+            boolean hideSnoozeButton = mN.isForegroundService() || mN.fullScreenIntent != null
+                    || isColorized(p) || p.mViewType == StandardTemplateParams.VIEW_TYPE_HEADS_UP;
+            big.setBoolean(R.id.snooze_button, "setEnabled", !hideSnoozeButton);
+            if (hideSnoozeButton) {
+                // Only hide; NotificationContentView will show it when it adds the click listener
+                big.setViewVisibility(R.id.snooze_button, View.GONE);
+            }
+
+            final boolean snoozeEnabled = !hideSnoozeButton
+                    && mContext.getContentResolver() != null
+                    && (Settings.Secure.getInt(mContext.getContentResolver(),
+                    Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
+            if (snoozeEnabled) {
+                big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, 0);
+            }
         }
 
         /**
@@ -5521,6 +5554,7 @@
             RemoteViews big = applyStandardTemplate(layoutId, p, result);
 
             resetStandardTemplateWithActions(big);
+            bindSnoozeAction(big, p);
 
             boolean validRemoteInput = false;
 
@@ -6834,26 +6868,13 @@
                 if (decorationType <= DevFlags.DECORATION_PARTIAL) {
                     template.removeFromParent(R.id.notification_top_line);
                 }
-                if (decorationType != DevFlags.DECORATION_FULL_COMPATIBLE) {
-                    // Change the max content size from 60dp (the compatible size) to 48dp
-                    // (the constrained size).  This is done by increasing the minimum margin
-                    // (implemented as top/bottom margins) and decreasing the extra margin
-                    // (implemented as the height of shrinkable top/bottom views in the column).
-                    template.setViewLayoutMarginDimen(
-                            R.id.notification_headerless_view_column,
-                            RemoteViews.MARGIN_TOP,
-                            R.dimen.notification_headerless_margin_constrained_minimum);
-                    template.setViewLayoutMarginDimen(
-                            R.id.notification_headerless_view_column,
-                            RemoteViews.MARGIN_BOTTOM,
-                            R.dimen.notification_headerless_margin_constrained_minimum);
-                    template.setViewLayoutHeightDimen(
-                            R.id.notification_headerless_margin_extra_top,
-                            R.dimen.notification_headerless_margin_constrained_extra);
-                    template.setViewLayoutHeightDimen(
-                            R.id.notification_headerless_margin_extra_bottom,
-                            R.dimen.notification_headerless_margin_constrained_extra);
-                }
+                // The vertical margins are bigger in the "two-line" scenario than the "one-line"
+                //  scenario, but the 'compatible' decoration state is intended to have 3 lines,
+                //  (1 for the top line views and 2 for the custom views), so in that one case we
+                //  use the smaller 1-line margins. This gives the compatible case 88-16*2=56 dp of
+                //  height, 24dp of which goes to the top line, leaving 32dp for the custom view.
+                boolean hasSecondLine = decorationType != DevFlags.DECORATION_FULL_COMPATIBLE;
+                Builder.setHeaderlessVerticalMargins(template, p, hasSecondLine);
             } else {
                 // also update the end margin to account for the large icon or expander
                 Resources resources = context.getResources();
@@ -9358,6 +9379,7 @@
 
             // Bind actions.
             mBuilder.resetStandardTemplateWithActions(contentView);
+            mBuilder.bindSnoozeAction(contentView, p);
             bindCallActions(contentView, p);
 
             // Bind some extra conversation-specific header fields.
@@ -11998,6 +12020,7 @@
         boolean mHideTitle;
         boolean mHideActions;
         boolean mHideProgress;
+        boolean mHideSnoozeButton;
         boolean mPromotePicture;
         boolean mAllowActionIcons;
         CharSequence title;
@@ -12015,6 +12038,7 @@
             mHideTitle = false;
             mHideActions = false;
             mHideProgress = false;
+            mHideSnoozeButton = false;
             mPromotePicture = false;
             mAllowActionIcons = false;
             title = null;
@@ -12061,6 +12085,11 @@
             return this;
         }
 
+        final StandardTemplateParams hideSnoozeButton(boolean hideSnoozeButton) {
+            this.mHideSnoozeButton = hideSnoozeButton;
+            return this;
+        }
+
         final StandardTemplateParams promotePicture(boolean promotePicture) {
             this.mPromotePicture = promotePicture;
             return this;
diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java
index 132af4b..dd2ba7d 100644
--- a/core/java/android/app/people/PeopleSpaceTile.java
+++ b/core/java/android/app/people/PeopleSpaceTile.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,7 +46,7 @@
     private String mId;
     private CharSequence mUserName;
     private Icon mUserIcon;
-    private int mUid;
+    private UserHandle mUserHandle;
     private Uri mContactUri;
     private String mPackageName;
     private String mBirthdayText;
@@ -64,7 +65,7 @@
         mUserName = b.mUserName;
         mUserIcon = b.mUserIcon;
         mContactUri = b.mContactUri;
-        mUid = b.mUid;
+        mUserHandle = b.mUserHandle;
         mPackageName = b.mPackageName;
         mBirthdayText = b.mBirthdayText;
         mLastInteractionTimestamp = b.mLastInteractionTimestamp;
@@ -95,8 +96,8 @@
         return mContactUri;
     }
 
-    public int getUid() {
-        return mUid;
+    public UserHandle getUserHandle() {
+        return mUserHandle;
     }
 
     public String getPackageName() {
@@ -165,7 +166,7 @@
         Builder builder =
                 new Builder(mId, mUserName.toString(), mUserIcon, mIntent);
         builder.setContactUri(mContactUri);
-        builder.setUid(mUid);
+        builder.setUserHandle(mUserHandle);
         builder.setPackageName(mPackageName);
         builder.setBirthdayText(mBirthdayText);
         builder.setLastInteractionTimestamp(mLastInteractionTimestamp);
@@ -186,7 +187,7 @@
         private CharSequence mUserName;
         private Icon mUserIcon;
         private Uri mContactUri;
-        private int mUid;
+        private UserHandle mUserHandle;
         private String mPackageName;
         private String mBirthdayText;
         private long mLastInteractionTimestamp;
@@ -212,7 +213,7 @@
             mId = info.getId();
             mUserName = info.getLabel();
             mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
-            mUid = info.getUserId();
+            mUserHandle = info.getUserHandle();
             mPackageName = info.getPackage();
             mContactUri = getContactUri(info);
         }
@@ -222,7 +223,7 @@
             mId = info.getId();
             mUserName = info.getLabel();
             mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
-            mUid = info.getUserId();
+            mUserHandle = info.getUserHandle();
             mPackageName = info.getPackage();
             mContactUri = getContactUri(info);
             mStatuses = channel.getStatuses();
@@ -265,9 +266,9 @@
             return this;
         }
 
-        /** Sets the associated uid. */
-        public Builder setUid(int uid) {
-            mUid = uid;
+        /** Sets the associated {@code userHandle}. */
+        public Builder setUserHandle(UserHandle userHandle) {
+            mUserHandle = userHandle;
             return this;
         }
 
@@ -349,7 +350,7 @@
         mUserName = in.readCharSequence();
         mUserIcon = in.readParcelable(Icon.class.getClassLoader());
         mContactUri = in.readParcelable(Uri.class.getClassLoader());
-        mUid = in.readInt();
+        mUserHandle = in.readParcelable(UserHandle.class.getClassLoader());
         mPackageName = in.readString();
         mBirthdayText = in.readString();
         mLastInteractionTimestamp = in.readLong();
@@ -375,7 +376,7 @@
         dest.writeCharSequence(mUserName);
         dest.writeParcelable(mUserIcon, flags);
         dest.writeParcelable(mContactUri, flags);
-        dest.writeInt(mUid);
+        dest.writeParcelable(mUserHandle, flags);
         dest.writeString(mPackageName);
         dest.writeString(mBirthdayText);
         dest.writeLong(mLastInteractionTimestamp);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 29a55b7..b345748 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -78,4 +78,7 @@
     ParceledListSlice getShortcuts(String packageName, int matchFlags, int userId);
 
     void pushDynamicShortcut(String packageName, in ShortcutInfo shortcut, int userId);
+
+    void updateShortcutVisibility(String callingPkg, String packageName, in byte[] certificate,
+            in boolean visible, int userId);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 567501c..42cbe35 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1173,7 +1173,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public @Nullable DataLoaderParams getDataLoaderParams() {
             try {
                 DataLoaderParamsParcel data = mSession.getDataLoaderParams();
@@ -1213,7 +1212,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
                 @NonNull byte[] metadata, @Nullable byte[] signature) {
             try {
@@ -1237,7 +1235,6 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void removeFile(@FileLocation int location, @NonNull String name) {
             try {
                 mSession.removeFile(location, name);
@@ -2050,9 +2047,7 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(allOf = {
-                Manifest.permission.INSTALL_PACKAGES,
-                Manifest.permission.USE_INSTALLER_V2})
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
         public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
             this.dataLoaderParams = dataLoaderParams;
         }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ca88241..29dea6b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4075,16 +4075,6 @@
     public static final int FLAG_PERMISSION_AUTO_REVOKED = 1 << 17;
 
     /**
-     * Permission flag: The permission is restricted but the app is exempt
-     * from the restriction and is allowed to hold this permission in its
-     * full form and the exemption is provided by the held roles.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT =  1 << 18;
-
-    /**
      * Permission flag: This location permission is selected as the level of granularity of
      * location accuracy.
      * Example: If this flag is set for ACCESS_FINE_LOCATION, FINE location is the selected location
@@ -4113,8 +4103,7 @@
     public static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
             FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT
                     | FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
-                    | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
-                    | FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                    | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
 
     /**
      * Mask for all permission flags.
@@ -4199,20 +4188,11 @@
      */
     public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 1 << 2;
 
-    /**
-     * Permission allowlist flag: permissions exempted by the system
-     * when being granted a role.
-     * Permissions can also be exempted by the installer, the system, or on
-     * upgrade.
-     */
-    public static final int FLAG_PERMISSION_ALLOWLIST_ROLE = 1 << 3;
-
     /** @hide */
     @IntDef(flag = true, prefix = {"FLAG_PERMISSION_WHITELIST_"}, value = {
             FLAG_PERMISSION_WHITELIST_SYSTEM,
             FLAG_PERMISSION_WHITELIST_INSTALLER,
-            FLAG_PERMISSION_WHITELIST_UPGRADE,
-            FLAG_PERMISSION_ALLOWLIST_ROLE
+            FLAG_PERMISSION_WHITELIST_UPGRADE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PermissionWhitelistFlags {}
@@ -5244,10 +5224,6 @@
      * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag.
      * Can be accessed by pre-installed holders of a dedicated permission or the
      * installer on record.
-     *
-     * <li>one for cases where the system exempts the permission when granting a role.
-     * This list corresponds to the {@link #FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can
-     * be accessed by pre-installed holders of a dedicated permission.
      * </ol>
      *
      * <p>
@@ -5266,7 +5242,6 @@
      * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see #FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @throws SecurityException if you try to access a whitelist that you have no access to.
      */
@@ -5306,10 +5281,6 @@
      * This list corresponds to the {@link #FLAG_PERMISSION_WHITELIST_INSTALLER} flag.
      * Can be modified by pre-installed holders of a dedicated permission or the installer
      * on record.
-     *
-     * <li>one for cases where the system exempts the permission when permission when
-     * granting a role. This list corresponds to the {@link #FLAG_PERMISSION_ALLOWLIST_ROLE}
-     * flag. Can be modified by pre-installed holders of a dedicated permission.
      * </ol>
      *
      * <p>You need to specify the whitelists for which to set the whitelisted permissions
@@ -5333,7 +5304,6 @@
      * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see #FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @throws SecurityException if you try to modify a whitelist that you have no access to.
      */
@@ -5403,7 +5373,6 @@
      * @see #FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see #FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see #FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see #FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @throws SecurityException if you try to modify a whitelist that you have no access to.
      */
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 0e70a3e..83baca6 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -411,14 +411,6 @@
     public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4;
 
     /**
-     * Flag for {@link #flags}, corresponding to <code>installerExemptIgnored</code>
-     * value of {@link android.R.attr#permissionFlags}.
-     *
-     * <p> Modifier for permission restriction. This permission cannot be exempted by the installer.
-     */
-    public static final int FLAG_INSTALLER_EXEMPT_IGNORED = 1 << 5;
-
-    /**
      * Flag for {@link #flags}, indicating that this permission has been
      * installed into the system's globally defined permissions.
      */
@@ -724,11 +716,6 @@
     }
 
     /** @hide */
-    public boolean isInstallerExemptIgnored() {
-        return (flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
-    }
-
-    /** @hide */
     public boolean isAppOp() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
     }
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index 35c99a1..d3bac79 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -39,6 +39,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -771,4 +772,20 @@
         }
     }
 
+    /**
+     * Granting another app the access to the shortcuts you own. You must provide the package name
+     * and their SHA256 certificate digest in order to granting the access.
+     *
+     * Once granted, the other app can retain a copy of all the shortcuts you own when calling
+     * {@link LauncherApps#getShortcuts(LauncherApps.ShortcutQuery, UserHandle)}.
+     */
+    public void updateShortcutVisibility(@NonNull final String packageName,
+            @Nullable final byte[] certificate, final boolean visible) {
+        try {
+            mService.updateShortcutVisibility(mContext.getPackageName(), packageName, certificate,
+                    visible, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index d81dff8..cfb6e1b 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -47,6 +48,7 @@
  *
  * @hide
  */
+@TestApi
 public class UserInfo implements Parcelable {
 
     /**
diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.aidl b/core/java/android/content/pm/verify/domain/DomainOwner.aidl
new file mode 100644
index 0000000..41366d1
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainOwner.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+parcelable DomainOwner;
diff --git a/core/java/android/content/pm/verify/domain/DomainOwner.java b/core/java/android/content/pm/verify/domain/DomainOwner.java
new file mode 100644
index 0000000..b050f5d
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainOwner.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+@SystemApi
+@DataClass(genParcelable = true, genEqualsHashCode = true, genAidl = true, genToString = true)
+public final class DomainOwner implements Parcelable {
+
+    /**
+     * Package name of that owns the domain.
+     */
+    @NonNull
+    private final String mPackageName;
+
+    /**
+     * Whether or not this owner can be automatically overridden.
+     *
+     * @see DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)
+     */
+    private final boolean mOverrideable;
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new DomainOwner.
+     *
+     * @param packageName
+     *   Package name of that owns the domain.
+     * @param overrideable
+     *   Whether or not this owner can be automatically overridden. If all owners for a domain are
+     *   overrideable, then calling
+     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
+     *   Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
+     *   of the owners are non-overrideable, then
+     *   {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
+     *   boolean)} must be called with false to disable all of the other owners before this domain can
+     *   be taken by a new owner through
+     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
+     *   Set, boolean)}.
+     */
+    @DataClass.Generated.Member
+    public DomainOwner(
+            @NonNull String packageName,
+            boolean overrideable) {
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mOverrideable = overrideable;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Package name of that owns the domain.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Whether or not this owner can be automatically overridden. If all owners for a domain are
+     * overrideable, then calling
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
+     * Set, boolean)} to enable the domain will disable all other owners. On the other hand, if any
+     * of the owners are non-overrideable, then
+     * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
+     * boolean)} must be called with false to disable all of the other owners before this domain can
+     * be taken by a new owner through
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID,
+     * Set, boolean)}.
+     */
+    @DataClass.Generated.Member
+    public boolean isOverrideable() {
+        return mOverrideable;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "DomainOwner { " +
+                "packageName = " + mPackageName + ", " +
+                "overrideable = " + mOverrideable +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainOwner other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainOwner that = (DomainOwner) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mPackageName, that.mPackageName)
+                && mOverrideable == that.mOverrideable;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
+        _hash = 31 * _hash + Boolean.hashCode(mOverrideable);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mOverrideable) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeString(mPackageName);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ DomainOwner(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        boolean overrideable = (flg & 0x2) != 0;
+        String packageName = in.readString();
+
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mOverrideable = overrideable;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DomainOwner> CREATOR
+            = new Parcelable.Creator<DomainOwner>() {
+        @Override
+        public DomainOwner[] newArray(int size) {
+            return new DomainOwner[size];
+        }
+
+        @Override
+        public DomainOwner createFromParcel(@NonNull android.os.Parcel in) {
+            return new DomainOwner(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1614119379978L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainOwner.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final  boolean mOverrideable\nclass DomainOwner extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genEqualsHashCode=true, genAidl=true, genToString=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainSet.aidl b/core/java/android/content/pm/verify/domain/DomainSet.aidl
new file mode 100644
index 0000000..fab131d
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainSet.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+parcelable DomainSet;
diff --git a/core/java/android/content/pm/verify/domain/DomainSet.java b/core/java/android/content/pm/verify/domain/DomainSet.java
new file mode 100644
index 0000000..243ff08
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainSet.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Set;
+
+/**
+ * Wraps an input set of domains from the client process, to be sent to the server. Handles cases
+ * where the data size is too large by writing data using {@link Parcel#writeBlob(byte[])}.
+ *
+ * @hide
+ */
+@DataClass(genParcelable = true, genAidl = true, genEqualsHashCode = true)
+public class DomainSet implements Parcelable {
+
+    @NonNull
+    private final Set<String> mDomains;
+
+    private void parcelDomains(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) {
+        DomainVerificationUtils.writeHostSet(dest, mDomains);
+    }
+
+    private Set<String> unparcelDomains(@NonNull Parcel in) {
+        return DomainVerificationUtils.readHostSet(in);
+    }
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
+    // /DomainSet.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public DomainSet(
+            @NonNull Set<String> domains) {
+        this.mDomains = domains;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDomains);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Set<String> getDomains() {
+        return mDomains;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(DomainSet other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        DomainSet that = (DomainSet) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mDomains, that.mDomains);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mDomains);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        parcelDomains(dest, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected DomainSet(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        Set<String> domains = unparcelDomains(in);
+
+        this.mDomains = domains;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDomains);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<DomainSet> CREATOR
+            = new Parcelable.Creator<DomainSet>() {
+        @Override
+        public DomainSet[] newArray(int size) {
+            return new DomainSet[size];
+        }
+
+        @Override
+        public DomainSet createFromParcel(@NonNull Parcel in) {
+            return new DomainSet(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1613169242020L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainSet.java",
+            inputSignatures = "private final @android.annotation.NonNull java.util.Set<java.lang.String> mDomains\nprivate  void parcelDomains(android.os.Parcel,int)\nprivate  java.util.Set<java.lang.String> unparcelDomains(android.os.Parcel)\nclass DomainSet extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
index 7afbe1f..8095875 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.pm.PackageManager;
+import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
@@ -34,12 +36,12 @@
  * against the digital asset links response from the server hosting that domain.
  * <p>
  * These values for each domain can be modified through
- * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)}.
+ * {@link DomainVerificationManager#setDomainVerificationStatus(UUID,
+ * Set, int)}.
  *
  * @hide
  */
 @SystemApi
-@SuppressWarnings("DefaultAnnotationParam")
 @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
         genEqualsHashCode = true)
 public final class DomainVerificationInfo implements Parcelable {
@@ -71,22 +73,30 @@
     private final String mPackageName;
 
     /**
-     * Map of host names to their current state. State is an integer, which defaults to
-     * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
-     * domain verification agent (the intended consumer of this API), which can be equal
-     * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
-     * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
-     * any unsuccessful response.
+     * Map of host names to their current state. State is an integer, which defaults to {@link
+     * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
+     * verification agent (the intended consumer of this API), which can be equal to {@link
+     * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
+     * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      * <p>
-     * Any value non-inclusive between those 2 values are reserved for use by the system.
-     * The domain verification agent may be able to act on these reserved values, and this
-     * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
-     * It is expected that the agent attempt to verify all domains that it can modify the
-     * state of, even if it does not understand the meaning of those values.
+     * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
+     * verification agent may be able to act on these reserved values, and this ability can be
+     * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
+     * the agent attempt to verify all domains that it can modify the state of, even if it does not
+     * understand the meaning of those values.
      */
     @NonNull
     private final Map<String, Integer> mHostToStateMap;
 
+    private void parcelHostToStateMap(Parcel dest, @SuppressWarnings("unused") int flags) {
+        DomainVerificationUtils.writeHostMap(dest, mHostToStateMap);
+    }
+
+    private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
+        return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
+                DomainVerificationUserSelection.class.getClassLoader());
+    }
+
 
 
     // Code below generated by codegen v1.0.22.
@@ -95,7 +105,8 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
+    // /DomainVerificationInfo.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -123,18 +134,17 @@
      * @param packageName
      *   The package name that this data corresponds to.
      * @param hostToStateMap
-     *   Map of host names to their current state. State is an integer, which defaults to
-     *   {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
-     *   domain verification agent (the intended consumer of this API), which can be equal
-     *   to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
-     *   greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
-     *   any unsuccessful response.
+     *   Map of host names to their current state. State is an integer, which defaults to {@link
+     *   DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
+     *   verification agent (the intended consumer of this API), which can be equal to {@link
+     *   DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
+     *   DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      *   <p>
-     *   Any value non-inclusive between those 2 values are reserved for use by the system.
-     *   The domain verification agent may be able to act on these reserved values, and this
-     *   ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
-     *   It is expected that the agent attempt to verify all domains that it can modify the
-     *   state of, even if it does not understand the meaning of those values.
+     *   Any value non-inclusive between those 2 values are reserved for use by the system. The domain
+     *   verification agent may be able to act on these reserved values, and this ability can be
+     *   queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
+     *   the agent attempt to verify all domains that it can modify the state of, even if it does not
+     *   understand the meaning of those values.
      * @hide
      */
     @DataClass.Generated.Member
@@ -185,18 +195,17 @@
     }
 
     /**
-     * Map of host names to their current state. State is an integer, which defaults to
-     * {@link DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the
-     * domain verification agent (the intended consumer of this API), which can be equal
-     * to {@link DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or
-     * greater than {@link DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for
-     * any unsuccessful response.
+     * Map of host names to their current state. State is an integer, which defaults to {@link
+     * DomainVerificationManager#STATE_NO_RESPONSE}. State can be modified by the domain
+     * verification agent (the intended consumer of this API), which can be equal to {@link
+     * DomainVerificationManager#STATE_SUCCESS} when verified, or equal to or greater than {@link
+     * DomainVerificationManager#STATE_FIRST_VERIFIER_DEFINED} for any unsuccessful response.
      * <p>
-     * Any value non-inclusive between those 2 values are reserved for use by the system.
-     * The domain verification agent may be able to act on these reserved values, and this
-     * ability can be queried using {@link DomainVerificationManager#isStateModifiable(int)}.
-     * It is expected that the agent attempt to verify all domains that it can modify the
-     * state of, even if it does not understand the meaning of those values.
+     * Any value non-inclusive between those 2 values are reserved for use by the system. The domain
+     * verification agent may be able to act on these reserved values, and this ability can be
+     * queried using {@link DomainVerificationManager#isStateModifiable(int)}. It is expected that
+     * the agent attempt to verify all domains that it can modify the state of, even if it does not
+     * understand the meaning of those values.
      */
     @DataClass.Generated.Member
     public @NonNull Map<String,Integer> getHostToStateMap() {
@@ -260,13 +269,13 @@
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
         dest.writeString(mPackageName);
-        dest.writeMap(mHostToStateMap);
+        parcelHostToStateMap(dest, flags);
     }
 
     @Override
@@ -276,14 +285,13 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ DomainVerificationInfo(@NonNull android.os.Parcel in) {
+    /* package-private */ DomainVerificationInfo(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         UUID identifier = sParcellingForIdentifier.unparcel(in);
         String packageName = in.readString();
-        Map<String,Integer> hostToStateMap = new java.util.LinkedHashMap<>();
-        in.readMap(hostToStateMap, Integer.class.getClassLoader());
+        Map<String,Integer> hostToStateMap = unparcelHostToStateMap(in);
 
         this.mIdentifier = identifier;
         com.android.internal.util.AnnotationValidations.validate(
@@ -307,16 +315,16 @@
         }
 
         @Override
-        public DomainVerificationInfo createFromParcel(@NonNull android.os.Parcel in) {
+        public DomainVerificationInfo createFromParcel(@NonNull Parcel in) {
             return new DomainVerificationInfo(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1611862790369L,
+            time = 1613002530369L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationInfo.java",
-            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate  java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
index cbb3baa..11402af 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManager.java
@@ -239,7 +239,15 @@
      * {@link Context#createPackageContextAsUser(String, int, UserHandle)} should be used.
      *
      * Enabling an unverified domain will allow an application to open it, but this can only occur
-     * if no other app on the device is approved for the domain.
+     * if no other app on the device is approved for a higher approval level. This can queried
+     * using {@link #getOwnersForDomain(String)}.
+     *
+     * If all owners for a domain are {@link DomainOwner#isOverrideable()}, then calling this to
+     * enable that domain will disable all other owners.
+     *
+     * On the other hand, if any of the owners are non-overrideable, then this must be called with
+     * false for all of the other owners to disable them before the domain can be taken by a new
+     * owner.
      *
      * @param domainSetId See {@link DomainVerificationInfo#getIdentifier()}.
      * @param domains     The domains to toggle the state of.
@@ -276,6 +284,19 @@
             throws NameNotFoundException;
 
     /**
+     * For the given domain, return all apps which are approved to open it in a
+     * greater than 0 priority. This does not mean that all apps can actually open
+     * an Intent with that domain. That will be decided by the set of apps which
+     * are the highest priority level, ignoring all lower priority levels.
+     *
+     * By default the list will be returned ordered from lowest to highest
+     * priority.
+     */
+    @NonNull
+    @RequiresPermission(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION)
+    List<DomainOwner> getOwnersForDomain(@NonNull String domain);
+
+    /**
      * Thrown if a {@link DomainVerificationInfo#getIdentifier()}} or an associated set of domains
      * provided by the caller is no longer valid. This may be recoverable, and the caller should
      * re-query the package name associated with the ID using
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
index 5938def..8b9865c 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationManagerImpl.java
@@ -21,11 +21,9 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -89,7 +87,7 @@
             int state) throws IllegalArgumentException, NameNotFoundException {
         try {
             mDomainVerificationManager.setDomainVerificationStatus(domainSetId.toString(),
-                    new ArrayList<>(domains), state);
+                    new DomainSet(domains), state);
         } catch (Exception e) {
             Exception converted = rethrow(e, domainSetId);
             if (converted instanceof NameNotFoundException) {
@@ -126,7 +124,7 @@
             throws IllegalArgumentException, NameNotFoundException {
         try {
             mDomainVerificationManager.setDomainVerificationUserSelection(domainSetId.toString(),
-                    new ArrayList<>(domains), enabled, mContext.getUserId());
+                    new DomainSet(domains), enabled, mContext.getUserId());
         } catch (Exception e) {
             Exception converted = rethrow(e, domainSetId);
             if (converted instanceof NameNotFoundException) {
@@ -158,6 +156,16 @@
         }
     }
 
+    @NonNull
+    @Override
+    public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
+        try {
+            return mDomainVerificationManager.getOwnersForDomain(domain, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private Exception rethrow(Exception exception, @Nullable UUID domainSetId) {
         return rethrow(exception, domainSetId, null);
     }
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
index 473abce..65f6d7c 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Intent;
+import android.os.Parcel;
 import android.os.Parcelable;
 
 import com.android.internal.util.DataClass;
@@ -27,11 +28,11 @@
 import java.util.Set;
 
 /**
- * Request object sent in the {@link Intent} that's broadcast to the domain verification
- * agent, retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}.
+ * Request object sent in the {@link Intent} that's broadcast to the domain verification agent,
+ * retrieved through {@link DomainVerificationManager#EXTRA_VERIFICATION_REQUEST}.
  * <p>
- * This contains the set of packages which have been invalidated and will require
- * re-verification. The exact domains can be retrieved with
+ * This contains the set of packages which have been invalidated and will require re-verification.
+ * The exact domains can be retrieved with
  * {@link DomainVerificationManager#getDomainVerificationInfo(String)}
  *
  * @hide
@@ -42,14 +43,22 @@
 public final class DomainVerificationRequest implements Parcelable {
 
     /**
-     * The package names of the apps that need to be verified. The receiver should call
-     * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
-     * these values to get the actual set of domains that need to be acted on.
+     * The package names of the apps that need to be verified. The receiver should call {@link
+     * DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get
+     * the actual set of domains that need to be acted on.
      */
     @NonNull
     @DataClass.ParcelWith(Parcelling.BuiltIn.ForStringSet.class)
     private final Set<String> mPackageNames;
 
+    private void parcelPackageNames(@NonNull Parcel dest, @SuppressWarnings("unused") int flags) {
+        DomainVerificationUtils.writeHostSet(dest, mPackageNames);
+    }
+
+    private Set<String> unparcelPackageNames(@NonNull Parcel in) {
+        return DomainVerificationUtils.readHostSet(in);
+    }
+
 
 
     // Code below generated by codegen v1.0.22.
@@ -58,7 +67,8 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
+    // /DomainVerificationRequest.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -69,9 +79,9 @@
      * Creates a new DomainVerificationRequest.
      *
      * @param packageNames
-     *   The package names of the apps that need to be verified. The receiver should call
-     *   {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
-     *   these values to get the actual set of domains that need to be acted on.
+     *   The package names of the apps that need to be verified. The receiver should call {@link
+     *   DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get
+     *   the actual set of domains that need to be acted on.
      * @hide
      */
     @DataClass.Generated.Member
@@ -85,9 +95,9 @@
     }
 
     /**
-     * The package names of the apps that need to be verified. The receiver should call
-     * {@link DomainVerificationManager#getDomainVerificationInfo(String)} with each of
-     * these values to get the actual set of domains that need to be acted on.
+     * The package names of the apps that need to be verified. The receiver should call {@link
+     * DomainVerificationManager#getDomainVerificationInfo(String)} with each of these values to get
+     * the actual set of domains that need to be acted on.
      */
     @DataClass.Generated.Member
     public @NonNull Set<String> getPackageNames() {
@@ -134,11 +144,11 @@
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        sParcellingForPackageNames.parcel(mPackageNames, dest, flags);
+        parcelPackageNames(dest, flags);
     }
 
     @Override
@@ -148,11 +158,11 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ DomainVerificationRequest(@NonNull android.os.Parcel in) {
+    /* package-private */ DomainVerificationRequest(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        Set<String> packageNames = sParcellingForPackageNames.unparcel(in);
+        Set<String> packageNames = unparcelPackageNames(in);
 
         this.mPackageNames = packageNames;
         com.android.internal.util.AnnotationValidations.validate(
@@ -170,16 +180,16 @@
         }
 
         @Override
-        public DomainVerificationRequest createFromParcel(@NonNull android.os.Parcel in) {
+        public DomainVerificationRequest createFromParcel(@NonNull Parcel in) {
             return new DomainVerificationRequest(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1611862814990L,
+            time = 1613169505495L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationRequest.java",
-            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)")
+            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForStringSet.class) java.util.Set<java.lang.String> mPackageNames\nprivate  void parcelPackageNames(android.os.Parcel,int)\nprivate  java.util.Set<java.lang.String> unparcelPackageNames(android.os.Parcel)\nclass DomainVerificationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genAidl=false, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
index 73346ef..d23f5f1 100644
--- a/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
@@ -20,8 +20,10 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling;
@@ -40,29 +42,46 @@
  * toggle affects <b>all</b> links and is not based on the verification state of the domains.
  * <p>
  * Assuming the toggle is enabled, the user can also select additional unverified domains to grant
- * to the application to open, which is reflected in {@link #getHostToUserSelectionMap()}. But only
- * a single application can be approved for a domain unless the applications are both approved. If
- * another application is approved, the user will not be allowed to enable the domain.
+ * to the application to open, which is reflected in {@link #getHostToStateMap()}. But only a single
+ * application can be approved for a domain unless the applications are both approved. If another
+ * application is approved, the user will not be allowed to enable the domain.
  * <p>
  * These values can be changed through the
  * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String,
- * boolean)} and
- * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
+ * boolean)} and {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
  * boolean)} APIs.
  * <p>
- * Note that because state is per user, if a different user needs to be changed, one will
- * need to use {@link Context#createContextAsUser(UserHandle, int)} and hold the
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ * Note that because state is per user, if a different user needs to be changed, one will need to
+ * use {@link Context#createContextAsUser(UserHandle, int)} and hold the {@link
+ * android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
  *
  * @hide
  */
 @SystemApi
 @SuppressWarnings("DefaultAnnotationParam")
 @DataClass(genAidl = true, genHiddenConstructor = true, genParcelable = true, genToString = true,
-        genEqualsHashCode = true)
+        genEqualsHashCode = true, genHiddenConstDefs = true)
 public final class DomainVerificationUserSelection implements Parcelable {
 
     /**
+     * The domain is unverified and unselected, and the application is unable to open web links
+     * that resolve to the domain.
+     */
+    public static final int DOMAIN_STATE_NONE = 0;
+
+    /**
+     * The domain has been selected through the
+     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)}
+     * API, under the assumption it has not been reset by the system.
+     */
+    public static final int DOMAIN_STATE_SELECTED = 1;
+
+    /**
+     * The domain has been previously verified by the domain verification agent.
+     */
+    public static final int DOMAIN_STATE_VERIFIED = 2;
+
+    /**
      * @see DomainVerificationInfo#getIdentifier
      */
     @NonNull
@@ -88,15 +107,20 @@
     private final boolean mLinkHandlingAllowed;
 
     /**
-     * Retrieve the existing user selection state for the matching
-     * {@link #getPackageName()}, as was previously set by
-     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
-     * boolean)}.
-     *
-     * @return Map of hosts to enabled state for the given package and user.
+     * Mapping of domain host to state, as defined by {@link DomainState}.
      */
     @NonNull
-    private final Map<String, Boolean> mHostToUserSelectionMap;
+    private final Map<String, Integer> mHostToStateMap;
+
+    private void parcelHostToStateMap(Parcel dest, @SuppressWarnings("unused") int flags) {
+        DomainVerificationUtils.writeHostMap(dest, mHostToStateMap);
+    }
+
+    @NonNull
+    private Map<String, Integer> unparcelHostToStateMap(Parcel in) {
+        return DomainVerificationUtils.readHostMap(in, new ArrayMap<>(),
+                DomainVerificationUserSelection.class.getClassLoader());
+    }
 
 
 
@@ -106,14 +130,37 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain
-    // /DomainVerificationUserSelection.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
     //@formatter:off
 
 
+    /** @hide */
+    @android.annotation.IntDef(prefix = "DOMAIN_STATE_", value = {
+        DOMAIN_STATE_NONE,
+        DOMAIN_STATE_SELECTED,
+        DOMAIN_STATE_VERIFIED
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface DomainState {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String domainStateToString(@DomainState int value) {
+        switch (value) {
+            case DOMAIN_STATE_NONE:
+                    return "DOMAIN_STATE_NONE";
+            case DOMAIN_STATE_SELECTED:
+                    return "DOMAIN_STATE_SELECTED";
+            case DOMAIN_STATE_VERIFIED:
+                    return "DOMAIN_STATE_VERIFIED";
+            default: return Integer.toHexString(value);
+        }
+    }
+
     /**
      * Creates a new DomainVerificationUserSelection.
      *
@@ -123,11 +170,8 @@
      *   The user that this data corresponds to.
      * @param linkHandlingAllowed
      *   Whether or not this package is allowed to open links.
-     * @param hostToUserSelectionMap
-     *   Retrieve the existing user selection state for the matching
-     *   {@link #getPackageName()}, as was previously set by
-     *   {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
-     *   boolean)}.
+     * @param hostToStateMap
+     *   Mapping of domain host to state, as defined by {@link DomainState}.
      * @hide
      */
     @DataClass.Generated.Member
@@ -136,7 +180,7 @@
             @NonNull String packageName,
             @NonNull UserHandle user,
             @NonNull boolean linkHandlingAllowed,
-            @NonNull Map<String,Boolean> hostToUserSelectionMap) {
+            @NonNull Map<String,Integer> hostToStateMap) {
         this.mIdentifier = identifier;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mIdentifier);
@@ -149,9 +193,9 @@
         this.mLinkHandlingAllowed = linkHandlingAllowed;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mLinkHandlingAllowed);
-        this.mHostToUserSelectionMap = hostToUserSelectionMap;
+        this.mHostToStateMap = hostToStateMap;
         com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mHostToUserSelectionMap);
+                NonNull.class, null, mHostToStateMap);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -189,16 +233,11 @@
     }
 
     /**
-     * Retrieve the existing user selection state for the matching
-     * {@link #getPackageName()}, as was previously set by
-     * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set,
-     * boolean)}.
-     *
-     * @return Map of hosts to enabled state for the given package and user.
+     * Mapping of domain host to state, as defined by {@link DomainState}.
      */
     @DataClass.Generated.Member
-    public @NonNull Map<String,Boolean> getHostToUserSelectionMap() {
-        return mHostToUserSelectionMap;
+    public @NonNull Map<String,Integer> getHostToStateMap() {
+        return mHostToStateMap;
     }
 
     @Override
@@ -212,7 +251,7 @@
                 "packageName = " + mPackageName + ", " +
                 "user = " + mUser + ", " +
                 "linkHandlingAllowed = " + mLinkHandlingAllowed + ", " +
-                "hostToUserSelectionMap = " + mHostToUserSelectionMap +
+                "hostToStateMap = " + mHostToStateMap +
         " }";
     }
 
@@ -233,7 +272,7 @@
                 && java.util.Objects.equals(mPackageName, that.mPackageName)
                 && java.util.Objects.equals(mUser, that.mUser)
                 && mLinkHandlingAllowed == that.mLinkHandlingAllowed
-                && java.util.Objects.equals(mHostToUserSelectionMap, that.mHostToUserSelectionMap);
+                && java.util.Objects.equals(mHostToStateMap, that.mHostToStateMap);
     }
 
     @Override
@@ -247,7 +286,7 @@
         _hash = 31 * _hash + java.util.Objects.hashCode(mPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mUser);
         _hash = 31 * _hash + Boolean.hashCode(mLinkHandlingAllowed);
-        _hash = 31 * _hash + java.util.Objects.hashCode(mHostToUserSelectionMap);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostToStateMap);
         return _hash;
     }
 
@@ -264,7 +303,7 @@
 
     @Override
     @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
@@ -274,7 +313,7 @@
         sParcellingForIdentifier.parcel(mIdentifier, dest, flags);
         dest.writeString(mPackageName);
         dest.writeTypedObject(mUser, flags);
-        dest.writeMap(mHostToUserSelectionMap);
+        parcelHostToStateMap(dest, flags);
     }
 
     @Override
@@ -284,7 +323,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ DomainVerificationUserSelection(@NonNull android.os.Parcel in) {
+    /* package-private */ DomainVerificationUserSelection(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -293,8 +332,7 @@
         UUID identifier = sParcellingForIdentifier.unparcel(in);
         String packageName = in.readString();
         UserHandle user = (UserHandle) in.readTypedObject(UserHandle.CREATOR);
-        Map<String,Boolean> hostToUserSelectionMap = new java.util.LinkedHashMap<>();
-        in.readMap(hostToUserSelectionMap, Boolean.class.getClassLoader());
+        Map<String,Integer> hostToStateMap = unparcelHostToStateMap(in);
 
         this.mIdentifier = identifier;
         com.android.internal.util.AnnotationValidations.validate(
@@ -308,9 +346,9 @@
         this.mLinkHandlingAllowed = linkHandlingAllowed;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mLinkHandlingAllowed);
-        this.mHostToUserSelectionMap = hostToUserSelectionMap;
+        this.mHostToStateMap = hostToStateMap;
         com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mHostToUserSelectionMap);
+                NonNull.class, null, mHostToStateMap);
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -324,16 +362,16 @@
         }
 
         @Override
-        public DomainVerificationUserSelection createFromParcel(@NonNull android.os.Parcel in) {
+        public DomainVerificationUserSelection createFromParcel(@NonNull Parcel in) {
             return new DomainVerificationUserSelection(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1612829797220L,
+            time = 1613683603297L,
             codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/verify/domain/DomainVerificationUserSelection.java",
-            inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Boolean> mHostToUserSelectionMap\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true)")
+            inputSignatures = "public static final  int DOMAIN_STATE_NONE\npublic static final  int DOMAIN_STATE_SELECTED\npublic static final  int DOMAIN_STATE_VERIFIED\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForUUID.class) java.util.UUID mIdentifier\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull android.os.UserHandle mUser\nprivate final @android.annotation.NonNull boolean mLinkHandlingAllowed\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> mHostToStateMap\nprivate  void parcelHostToStateMap(android.os.Parcel,int)\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.Integer> unparcelHostToStateMap(android.os.Parcel)\nclass DomainVerificationUserSelection extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=true, genHiddenConstructor=true, genParcelable=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java b/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java
new file mode 100644
index 0000000..93005fa
--- /dev/null
+++ b/core/java/android/content/pm/verify/domain/DomainVerificationUtils.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.verify.domain;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.ArraySet;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class DomainVerificationUtils {
+
+    private static final int STRINGS_TARGET_BYTE_SIZE = IBinder.getSuggestedMaxIpcSizeBytes() / 2;
+
+    /**
+     * Write a map containing web hosts to the given parcel, using {@link Parcel#writeBlob(byte[])}
+     * if the limit exceeds {@link IBinder#getSuggestedMaxIpcSizeBytes()} / 2. This assumes that the
+     * written map is the only data structure in the caller that varies based on the host data set.
+     * Other data that will be written to the parcel after this method will not be considered in the
+     * calculation.
+     */
+    public static void writeHostMap(@NonNull Parcel dest, @NonNull Map<String, ?> map) {
+        boolean targetSizeExceeded = false;
+        int totalSize = dest.dataSize();
+        for (String host : map.keySet()) {
+            totalSize += estimatedByteSizeOf(host);
+            if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
+                targetSizeExceeded = true;
+                break;
+            }
+        }
+
+        dest.writeBoolean(targetSizeExceeded);
+
+        if (!targetSizeExceeded) {
+            dest.writeMap(map);
+            return;
+        }
+
+        Parcel data = Parcel.obtain();
+        try {
+            data.writeMap(map);
+            dest.writeBlob(data.marshall());
+        } finally {
+            data.recycle();
+        }
+    }
+
+    /**
+     * Retrieve a map previously written by {@link #writeHostMap(Parcel, Map)}.
+     */
+    @NonNull
+    @SuppressWarnings("rawtypes")
+    public static <T extends Map> T readHostMap(@NonNull Parcel in, @NonNull T map,
+            @NonNull ClassLoader classLoader) {
+        boolean targetSizeExceeded = in.readBoolean();
+
+        if (!targetSizeExceeded) {
+            in.readMap(map, classLoader);
+            return map;
+        }
+
+        Parcel data = Parcel.obtain();
+        try {
+            byte[] blob = in.readBlob();
+            data.unmarshall(blob, 0, blob.length);
+            data.setDataPosition(0);
+            data.readMap(map, classLoader);
+        } finally {
+            data.recycle();
+        }
+
+        return map;
+    }
+
+    /**
+     * {@link ArraySet} variant of {@link #writeHostMap(Parcel, Map)}.
+     */
+    public static void writeHostSet(@NonNull Parcel dest, @NonNull Set<String> set) {
+        boolean targetSizeExceeded = false;
+        int totalSize = dest.dataSize();
+        for (String host : set) {
+            totalSize += estimatedByteSizeOf(host);
+            if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
+                targetSizeExceeded = true;
+                break;
+            }
+        }
+
+        dest.writeBoolean(targetSizeExceeded);
+
+        if (!targetSizeExceeded) {
+            writeSet(dest, set);
+            return;
+        }
+
+        Parcel data = Parcel.obtain();
+        try {
+            writeSet(data, set);
+            dest.writeBlob(data.marshall());
+        } finally {
+            data.recycle();
+        }
+    }
+
+    /**
+     * {@link ArraySet} variant of {@link #readHostMap(Parcel, Map, ClassLoader)}.
+     */
+    @NonNull
+    public static Set<String> readHostSet(@NonNull Parcel in) {
+        boolean targetSizeExceeded = in.readBoolean();
+
+        if (!targetSizeExceeded) {
+            return readSet(in);
+        }
+
+        Parcel data = Parcel.obtain();
+        try {
+            byte[] blob = in.readBlob();
+            data.unmarshall(blob, 0, blob.length);
+            data.setDataPosition(0);
+            return readSet(data);
+        } finally {
+            data.recycle();
+        }
+    }
+
+    private static void writeSet(@NonNull Parcel dest, @Nullable Set<String> set) {
+        if (set == null) {
+            dest.writeInt(-1);
+            return;
+        }
+        dest.writeInt(set.size());
+        for (String string : set) {
+            dest.writeString(string);
+        }
+    }
+
+    @NonNull
+    private static Set<String> readSet(@NonNull Parcel in) {
+        int size = in.readInt();
+        if (size == -1) {
+            return Collections.emptySet();
+        }
+
+        ArraySet<String> set = new ArraySet<>(size);
+        for (int count = 0; count < size; count++) {
+            set.add(in.readString());
+        }
+        return set;
+    }
+
+    /**
+     * Ballpark the size of domains to avoid unnecessary allocation of ashmem when sending domains
+     * across the client-server API.
+     */
+    public static int estimatedByteSizeOf(String string) {
+        return string.length() * 2 + 12;
+    }
+}
diff --git a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
index 21dd623b..701af32 100644
--- a/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
+++ b/core/java/android/content/pm/verify/domain/IDomainVerificationManager.aidl
@@ -16,6 +16,8 @@
 
 package android.content.pm.verify.domain;
 
+import android.content.pm.verify.domain.DomainOwner;
+import android.content.pm.verify.domain.DomainSet;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationUserSelection;
 import java.util.List;
@@ -35,10 +37,13 @@
     DomainVerificationUserSelection getDomainVerificationUserSelection(String packageName,
             int userId);
 
-    void setDomainVerificationStatus(String domainSetId, in List<String> domains, int state);
+    @nullable
+    List<DomainOwner> getOwnersForDomain(String domain, int userId);
+
+    void setDomainVerificationStatus(String domainSetId, in DomainSet domains, int state);
 
     void setDomainVerificationLinkHandlingAllowed(String packageName, boolean allowed, int userId);
 
-    void setDomainVerificationUserSelection(String domainSetId, in List<String> domains,
+    void setDomainVerificationUserSelection(String domainSetId, in DomainSet domains,
             boolean enabled, int userId);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index 663a704..51addc9 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -85,8 +85,8 @@
             boolean resetLockoutRequiresHardwareAuthToken) {
         // TODO(b/179175438): Value should be provided from the HAL
         this(sensorId, strength, maxEnrollmentsPerUser, sensorType,
-                resetLockoutRequiresHardwareAuthToken, 0 /* sensorLocationX */,
-                0 /* sensorLocationY */, 0 /* sensorRadius */);
+                resetLockoutRequiresHardwareAuthToken, 540 /* sensorLocationX */,
+                1636 /* sensorLocationY */, 130 /* sensorRadius */);
     }
 
     /**
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index 816bc6b..e5d0370 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+ytai@google.com
+elaurent@google.com
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 1bdc82a..97e03e9 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -38,6 +38,23 @@
           "include-filter": "com.android.server.pm.parsing.PackageInfoUserFieldsTest"
         }
       ]
+    },
+    {
+      "file_patterns": ["BatteryStats.java"],
+      "name": "FrameworksCoreTests",
+      "options": [
+        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
+        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+      ]
+    },
+    {
+      "file_patterns": ["BatteryStats.java"],
+      "name": "FrameworksServicesTests",
+      "options": [
+        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
+        { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" },
+        { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" }
+      ]
     }
   ],
   "postsubmit": [
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 8bdfd3d..682754e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1688,6 +1688,7 @@
      * @return Whether guest user is always ephemeral
      * @hide
      */
+    @TestApi
     public static boolean isGuestUserEphemeral() {
         return Resources.getSystem()
                 .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral);
@@ -1802,6 +1803,20 @@
     }
 
     /**
+     * @return the user type of the context user.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    @UserHandleAware
+    public @NonNull String getUserType() {
+        UserInfo userInfo = getUserInfo(mUserId);
+        return userInfo == null ? "" : userInfo.userType;
+    }
+
+    /**
      * Returns the user name of the context user. This call is only available to applications on
      * the system image; it requires the {@code android.permission.MANAGE_USERS} or {@code
      * android.permission.GET_ACCOUNTS_PRIVILEGED} permissions.
@@ -1809,7 +1824,8 @@
      * @return the user name
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}, conditional = true)
+            android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED,
+            android.Manifest.permission.CREATE_USERS}, conditional = true)
     @UserHandleAware
     public @NonNull String getUserName() {
         if (UserHandle.myUserId() == mUserId) {
@@ -2792,6 +2808,7 @@
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
+    @TestApi
     public @Nullable UserInfo createUser(@Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags) {
         try {
@@ -2828,6 +2845,7 @@
      * @throws UserOperationException if the user could not be created.
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
     public @NonNull UserInfo preCreateUser(@NonNull String userType)
@@ -2976,10 +2994,11 @@
      *
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createProfileForUser(String name, @NonNull String userType,
-            @UserInfoFlag int flags, @UserIdInt int userId, String[] disallowedPackages) {
+    public @Nullable UserInfo createProfileForUser(@Nullable String name, @NonNull String userType,
+            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages) {
         try {
             return mService.createProfileForUserWithThrow(name, userType, flags, userId,
                     disallowedPackages);
@@ -3022,9 +3041,10 @@
      *
      * @hide
      */
+    @TestApi
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createRestrictedProfile(String name) {
+    public @Nullable UserInfo createRestrictedProfile(@Nullable String name) {
         try {
             UserHandle parentUserHandle = Process.myUserHandle();
             UserInfo user = mService.createRestrictedProfileWithThrow(name,
@@ -3248,10 +3268,11 @@
 
     /**
      * Return the number of users currently created on the device.
-     * <p>This API is not for use by third-party apps. It requires the {@code MANAGE_USERS}
-     * permission.</p>
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public int getUserCount() {
         List<UserInfo> users = getUsers();
         return users != null ? users.size() : 1;
@@ -3274,7 +3295,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public List<UserInfo> getUsers() {
         return getUsers(/*excludePartial= */ true, /* excludeDying= */ false,
                 /* excludePreCreated= */ true);
@@ -3292,7 +3316,10 @@
      * @return the list of users that were created.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserInfo> getAliveUsers() {
         return getUsers(/*excludePartial= */ true, /* excludeDying= */ true,
                 /* excludePreCreated= */ true);
@@ -3306,7 +3333,10 @@
      */
     @Deprecated
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
         return getUsers(/*excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3317,8 +3347,12 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
-    public List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
+    @TestApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
+    public @NonNull List<UserInfo> getUsers(boolean excludePartial, boolean excludeDying,
             boolean excludePreCreated) {
         try {
             return mService.getUsers(excludePartial, excludeDying, excludePreCreated);
@@ -3335,7 +3369,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) {
         List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3354,7 +3391,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.CREATE_USERS
+    })
     public long[] getSerialNumbersOfUsers(boolean excludeDying) {
         List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
@@ -3678,7 +3718,10 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public UserInfo getProfileParent(@UserIdInt int userId) {
         try {
             return mService.getProfileParent(userId);
@@ -3697,7 +3740,10 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.MANAGE_USERS,
+            android.Manifest.permission.INTERACT_ACROSS_USERS
+    })
     public @Nullable UserHandle getProfileParent(@NonNull UserHandle user) {
         UserInfo info = getProfileParent(user.getIdentifier());
 
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 06203ff..9ffc5aa0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -426,7 +426,7 @@
         // avoid writing a partial response to the zygote.
         for (String arg : args) {
             // Making two indexOf calls here is faster than running a manually fused loop due
-            // to the fact that indexOf is a optimized intrinsic.
+            // to the fact that indexOf is an optimized intrinsic.
             if (arg.indexOf('\n') >= 0) {
                 throw new ZygoteStartFailedEx("Embedded newlines not allowed");
             } else if (arg.indexOf('\r') >= 0) {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index ff01011..bae36b29 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -485,10 +485,6 @@
      * One for cases where the installer of the package allowlists a permission. This list
      * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
      * accessed by pre-installed holders of a dedicated permission or the installer on record.
-     * <li>
-     * One for cases where the system exempts the permission when granting a role. This list
-     * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
-     * accessed by pre-installed holders of a dedicated permission.
      * </ol>
      *
      * @param packageName the app for which to get allowlisted permissions
@@ -502,7 +498,6 @@
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @hide Pending API
      */
@@ -549,10 +544,6 @@
      * One for cases where the installer of the package allowlists a permission. This list
      * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
      * accessed by pre-installed holders of a dedicated permission or the installer on record.
-     * <li>
-     * One for cases where the system exempts the permission when granting a role. This list
-     * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
-     * accessed by pre-installed holders of a dedicated permission.
      * </ol>
      * <p>
      * You need to specify the allowlists for which to set the allowlisted permissions which will
@@ -570,7 +561,6 @@
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @hide Pending API
      */
@@ -613,10 +603,6 @@
      * One for cases where the installer of the package allowlists a permission. This list
      * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
      * accessed by pre-installed holders of a dedicated permission or the installer on record.
-     * <li>
-     * One for cases where the system exempts the permission when granting a role. This list
-     * corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
-     * accessed by pre-installed holders of a dedicated permission.
      * </ol>
      * <p>
      * You need to specify the allowlists for which to set the allowlisted permissions which will
@@ -634,7 +620,6 @@
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
      * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
-     * @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
      *
      * @hide Pending API
      */
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 0e35ef9..4c9e77c 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -57,6 +57,8 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -186,6 +188,15 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
+    private boolean isSpeechRecognizerUsage(String op, String packageName) {
+        if (!OPSTR_RECORD_AUDIO.equals(op)) {
+            return false;
+        }
+
+        return packageName.equals(
+                mContext.getString(R.string.config_systemSpeechRecognizer));
+    }
+
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
@@ -317,7 +328,8 @@
                     if (packageName.equals(SYSTEM_PKG)
                             || (!isUserSensitive(packageName, user, op)
                             && !isLocationProvider(packageName, user)
-                            && !isAppPredictor(packageName, user))) {
+                            && !isAppPredictor(packageName, user))
+                            && !isSpeechRecognizerUsage(op, packageName)) {
                         continue;
                     }
 
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 4354920..6e89faf 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -157,6 +157,14 @@
     public static final String NAMESPACE_BLUETOOTH = "bluetooth";
 
     /**
+     * Namespace for features relating to clipboard.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_CLIPBOARD = "clipboard";
+
+    /**
      * Namespace for all networking connectivity related features.
      *
      * @hide
diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl
index d97e3c6..26eaac0 100644
--- a/core/java/android/view/IScrollCaptureCallbacks.aidl
+++ b/core/java/android/view/IScrollCaptureCallbacks.aidl
@@ -16,12 +16,10 @@
 
 package android.view;
 
-import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 
-import android.view.IScrollCaptureConnection;
-
 /**
  * Asynchronous callback channel for responses to scroll capture requests.
  *
@@ -29,34 +27,30 @@
  */
 interface IScrollCaptureCallbacks {
     /**
-     * Scroll capture is available, and a connection has been provided.
+     * Provides the result of WindowManagerService#requestScrollCapture
      *
-     * @param connection a connection to a window process and scrollable content
-     * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space
+     * @param response the response which describes the result
      */
-    oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds,
-            in Point positionInWindow);
+    oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
 
     /**
-     * The window does not support scroll capture.
-     */
-    oneway void onUnavailable();
-
-    /**
-     * Called when the remote end has confirmed the request and is ready to begin providing image
-     * requests.
+     * Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed
+     * the request and is ready to begin capturing images.
      */
     oneway void onCaptureStarted();
 
     /**
-     * Received a response from a capture request.
+     * Received a response from a capture request. The provided rectangle indicates the portion
+     * of the requested rectangle which was captured. An empty rectangle indicates that the request
+     * could not be satisfied (most commonly due to the available scrolling range).
+     *
+     * @param flags flags describing additional status of the result
+     * @param capturedArea the actual area of the image captured
      */
-    oneway void onCaptureBufferSent(long frameNumber, in Rect capturedArea);
+    oneway void onImageRequestCompleted(int flags, in Rect capturedArea);
 
     /**
-     * Signals that the capture session has completed and the target window may be returned to
-     * normal interactive use. This may be due to normal shutdown, or after a timeout or other
-     * unrecoverable state change such as activity lifecycle, window visibility or focus.
+     * Signals that the capture session has completed and the target window is ready for normal use.
      */
-    oneway void onConnectionClosed();
+    oneway void onCaptureEnded();
 }
diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl
index 63a4f48..c55e888 100644
--- a/core/java/android/view/IScrollCaptureConnection.aidl
+++ b/core/java/android/view/IScrollCaptureConnection.aidl
@@ -17,33 +17,45 @@
 package android.view;
 
 import android.graphics.Rect;
+import android.os.ICancellationSignal;
 import android.view.Surface;
 
 
  /**
-   * Interface implemented by a client of the Scroll Capture framework to receive requests
-   * to start, capture images and end the session.
+   * A remote connection to a scroll capture target.
    *
    * {@hide}
    */
 interface IScrollCaptureConnection {
 
     /**
-     * Informs the client that it has been selected for scroll capture and should prepare to
-     * to begin handling capture requests.
-     */
-    oneway void startCapture(in Surface surface);
-
-    /**
-     * Request the client capture an image within the provided rectangle.
+     * Informs the target that it has been selected for scroll capture.
      *
-     * @see android.view.ScrollCaptureCallback#onScrollCaptureRequest
+     * @param surface a return channel for image buffers
+     *
+     * @return a cancallation signal which is used cancel the request
      */
-    oneway void requestImage(in Rect captureArea);
+    ICancellationSignal startCapture(in Surface surface);
 
     /**
-     * Inform the client that capture has ended. The client should shut down and release all
-     * local resources in use and prepare for return to normal interactive usage.
+     * Request the target capture an image within the provided rectangle.
+     *
+     * @param surface a return channel for image buffers
+     * @param signal a cancallation signal which can interrupt the request
+     *
+     * @return a cancallation signal which is used cancel the request
      */
-    oneway void endCapture();
+    ICancellationSignal requestImage(in Rect captureArea);
+
+    /**
+     * Inform the target that capture has ended.
+     *
+     * @return a cancallation signal which is used cancel the request
+     */
+    ICancellationSignal endCapture();
+
+    /**
+     * Closes the connection.
+     */
+    oneway void close();
 }
diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java
index d3aad2c..1688616 100644
--- a/core/java/android/view/ScrollCaptureCallback.java
+++ b/core/java/android/view/ScrollCaptureCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.UiThread;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 
 import java.util.function.Consumer;
 
@@ -58,8 +59,6 @@
  * @see View#setScrollCaptureHint(int)
  * @see View#setScrollCaptureCallback(ScrollCaptureCallback)
  * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback)
- *
- * @hide
  */
 @UiThread
 public interface ScrollCaptureCallback {
@@ -68,80 +67,84 @@
      * The system is searching for the appropriate scrolling container to capture and would like to
      * know the size and position of scrolling content handled by this callback.
      * <p>
-     * Implementations should inset {@code containingViewBounds} to cover only the area within the
-     * containing view where scrolling content may be positioned. This should cover only the content
-     * which tracks with scrolling movement.
+     * To determine scroll bounds, an implementation should inset the visible bounds of the
+     * containing view to cover only the area where scrolling content may be positioned. This
+     * should cover only the content which tracks with scrolling movement.
      * <p>
-     * Return the updated rectangle to {@code resultConsumer}. If for any reason the scrolling
-     * content is not available to capture, a {@code null} rectangle may be returned, and this view
-     * will be excluded as the target for this request.
+     * Return the updated rectangle to {@link Consumer#accept onReady.accept}. If for any reason the
+     * scrolling content is not available to capture, a empty rectangle may be returned which will
+     * exclude this view from consideration.
      * <p>
-     * Responses received after XXXms will be discarded.
-     * <p>
-     * TODO: finalize timeout
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Consumer#accept onReady.accept} will have no effect and this
+     * content will be omitted from the search results.
      *
-     * @param onReady              consumer for the updated rectangle
+     * @param signal  signal to cancel the operation in progress
+     * @param onReady consumer for the updated rectangle
      */
-    void onScrollCaptureSearch(@NonNull Consumer<Rect> onReady);
+    void onScrollCaptureSearch(@NonNull CancellationSignal signal, @NonNull Consumer<Rect> onReady);
 
     /**
      * Scroll Capture has selected this callback to provide the scrolling image content.
      * <p>
-     * The onReady signal should be called when ready to begin handling image requests.
+     * {@link Runnable#run onReady.run} should be called when ready to begin handling image
+     * requests.
+     * <p>
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Runnable#run onReady.run} will have no effect and provided session
+     * will not be activated.
+     *
+     * @param session the current session, resources provided by it are valid for use until the
+     *                {@link #onScrollCaptureEnd(Runnable) session ends}
+     * @param signal  signal to cancel the operation in progress
+     * @param onReady signal used to report completion of the request
      */
-    void onScrollCaptureStart(@NonNull ScrollCaptureSession session, @NonNull Runnable onReady);
+    void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Runnable onReady);
 
     /**
      * An image capture has been requested from the scrolling content.
      * <p>
-     * <code>captureArea</code> contains the bounds of the image requested, relative to the
-     * rectangle provided by {@link ScrollCaptureCallback#onScrollCaptureSearch}, referred to as
-     * {@code scrollBounds}.
-     * here.
+     * The requested rectangle describes an area inside the target view, relative to
+     * <code>scrollBounds</code>. The content may be offscreen, above or below the current visible
+     * portion of the target view. To handle the request, render the available portion of this
+     * rectangle to a buffer and return it via the Surface available from {@link
+     * ScrollCaptureSession#getSurface()}.
      * <p>
-     * A series of requests will step by a constant vertical amount relative to {@code
-     * scrollBounds}, moving through the scrolling range of content, above and below the current
-     * visible area. The rectangle's vertical position will not account for any scrolling movement
-     * since capture started. Implementations therefore must track any scroll position changes and
-     * subtract this distance from requests.
+     * Note: Implementations are only required to render the requested content, and may do so into
+     * off-screen buffers without scrolling if they are able.
      * <p>
-     * To handle a request, the content should be scrolled to maximize the visible area of the
-     * requested rectangle. Offset {@code captureArea} again to account for any further scrolling.
+     * The resulting available portion of the request must be computed as a portion of {@code
+     * captureArea}, and sent to signal the operation is complete, using  {@link Consumer#accept
+     * onComplete.accept}. If the requested rectangle is  partially or fully out of bounds the
+     * resulting portion should be returned. If no portion is available (outside of available
+     * content), then skip sending any buffer and report an empty Rect as result.
      * <p>
-     * Finally, clip this rectangle against scrollBounds to determine what portion, if any is
-     * visible content to capture. If the rectangle is completely clipped, set it to {@link
-     * Rect#setEmpty() empty} and skip the next step.
-     * <p>
-     * Make a copy of {@code captureArea}, transform to window coordinates and draw the window,
-     * clipped to this rectangle, into the {@link ScrollCaptureSession#getSurface() surface} at
-     * offset (0,0).
-     * <p>
-     * Finally, return the resulting {@code captureArea} using
-     * {@link ScrollCaptureSession#notifyBufferSent}.
-     * <p>
-     * If the response is not supplied within XXXms, the session will end with a call to {@link
-     * #onScrollCaptureEnd}, after which {@code session} is invalid and should be discarded.
-     * <p>
-     * TODO: finalize timeout
-     * <p>
+     * This request may be cancelled via the provided {@link CancellationSignal}. When this happens,
+     * any future call to {@link Consumer#accept onComplete.accept} will be ignored until the next
+     * request.
      *
+     * @param session the current session, resources provided by it are valid for use until the
+     *                {@link #onScrollCaptureEnd(Runnable) session ends}
+     * @param signal      signal to cancel the operation in progress
      * @param captureArea the area to capture, a rectangle within {@code scrollBounds}
+     * @param onComplete  a consumer for the captured area
      */
-    void onScrollCaptureImageRequest(
-            @NonNull ScrollCaptureSession session, @NonNull Rect captureArea);
+    void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+            @NonNull Consumer<Rect> onComplete);
 
     /**
      * Signals that capture has ended. Implementations should release any temporary resources or
      * references to objects in use during the capture. Any resources obtained from the session are
      * now invalid and attempts to use them after this point may throw an exception.
      * <p>
-     * The window should be returned as much as possible to its original state when capture started.
-     * At a minimum, the content should be scrolled to its original position.
+     * The window should be returned to its original state when capture started. At a minimum, the
+     * content should be scrolled to its original position.
      * <p>
-     * <code>onReady</code> should be called when the window should be made visible and
-     * interactive. The system will wait up to XXXms for this call before proceeding.
-     * <p>
-     * TODO: finalize timeout
+     * {@link Runnable#run onReady.run} should be called as soon as possible after the window is
+     * ready for normal interactive use. After the callback (or after a timeout, if not called) the
+     * screenshot tool will be dismissed and the window may become visible to the user at any time.
      *
      * @param onReady a callback to inform the system that the application has completed any
      *                cleanup and is ready to become visible
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 0e6cdd1d..3456e01 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -18,18 +18,23 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.BinderThread;
 import android.annotation.NonNull;
 import android.annotation.UiThread;
-import android.annotation.WorkerThread;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Handler;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.CloseGuard;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Mediator between a selected scroll capture target view and a remote process.
@@ -41,270 +46,276 @@
 public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub {
 
     private static final String TAG = "ScrollCaptureConnection";
-    private static final int DEFAULT_TIMEOUT = 1000;
 
-    private final Handler mHandler;
-    private ScrollCaptureTarget mSelectedTarget;
-    private int mTimeoutMillis = DEFAULT_TIMEOUT;
-
-    protected Surface mSurface;
-    private IScrollCaptureCallbacks mCallbacks;
-
+    private final Object mLock = new Object();
     private final Rect mScrollBounds;
     private final Point mPositionInWindow;
     private final CloseGuard mCloseGuard;
+    private final Executor mUiThread;
 
-    // The current session instance in use by the callback.
+    private ScrollCaptureCallback mLocal;
+    private IScrollCaptureCallbacks mRemote;
+
     private ScrollCaptureSession mSession;
 
-    // Helps manage timeout callbacks registered to handler and aids testing.
-    private DelayedAction mTimeoutAction;
+    private CancellationSignal mCancellation;
+
+    private volatile boolean mStarted;
+    private volatile boolean mConnected;
 
     /**
      * Constructs a ScrollCaptureConnection.
      *
      * @param selectedTarget  the target the client is controlling
-     * @param callbacks the callbacks to reply to system requests
+     * @param remote the callbacks to reply to system requests
      *
      * @hide
      */
     public ScrollCaptureConnection(
+            @NonNull Executor uiThread,
             @NonNull ScrollCaptureTarget selectedTarget,
-            @NonNull IScrollCaptureCallbacks callbacks) {
+            @NonNull IScrollCaptureCallbacks remote) {
+        mUiThread = requireNonNull(uiThread, "<uiThread> must non-null");
         requireNonNull(selectedTarget, "<selectedTarget> must non-null");
-        requireNonNull(callbacks, "<callbacks> must non-null");
-        final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(),
+        mRemote = requireNonNull(remote, "<callbacks> must non-null");
+        mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()),
                 "target.getScrollBounds() must be non-null to construct a client");
 
-        mSelectedTarget = selectedTarget;
-        mHandler = selectedTarget.getContainingView().getHandler();
-        mScrollBounds = new Rect(scrollBounds);
+        mLocal = selectedTarget.getCallback();
         mPositionInWindow = new Point(selectedTarget.getPositionInWindow());
 
-        mCallbacks = callbacks;
         mCloseGuard = new CloseGuard();
         mCloseGuard.open("close");
-
-        selectedTarget.getContainingView().addOnAttachStateChangeListener(
-                new View.OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View v) {
-
-                    }
-
-                    @Override
-                    public void onViewDetachedFromWindow(View v) {
-                        selectedTarget.getContainingView().removeOnAttachStateChangeListener(this);
-                        endCapture();
-                    }
-                });
+        mConnected = true;
     }
 
-    @VisibleForTesting
-    public void setTimeoutMillis(int timeoutMillis) {
-        mTimeoutMillis = timeoutMillis;
-    }
-
-    @VisibleForTesting
-    public DelayedAction getTimeoutAction() {
-        return mTimeoutAction;
-    }
-
-    private void checkConnected() {
-        if (mSelectedTarget == null || mCallbacks == null) {
-            throw new IllegalStateException("This client has been disconnected.");
-        }
-    }
-
-    private void checkStarted() {
-        if (mSession == null) {
-            throw new IllegalStateException("Capture session has not been started!");
-        }
-    }
-
-    @WorkerThread // IScrollCaptureConnection
+    @BinderThread
     @Override
-    public void startCapture(Surface surface) throws RemoteException {
+    public ICancellationSignal startCapture(Surface surface) throws RemoteException {
         checkConnected();
-        mSurface = surface;
-        scheduleTimeout(mTimeoutMillis, this::onStartCaptureTimeout);
-        mSession = new ScrollCaptureSession(mSurface, mScrollBounds, mPositionInWindow, this);
-        mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureStart(mSession,
-                this::onStartCaptureCompleted));
+        if (!surface.isValid()) {
+            throw new RemoteException(new IllegalArgumentException("surface must be valid"));
+        }
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+        mSession = new ScrollCaptureSession(surface, mScrollBounds, mPositionInWindow);
+
+        Runnable listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onStartCaptureCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureStart(mSession, mCancellation, listener));
+        return cancellation;
     }
 
     @UiThread
     private void onStartCaptureCompleted() {
-        if (cancelTimeout()) {
-            mHandler.post(() -> {
-                try {
-                    mCallbacks.onCaptureStarted();
-                } catch (RemoteException e) {
-                    doShutdown();
-                }
-            });
+        mStarted = true;
+        try {
+            mRemote.onCaptureStarted();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Shutting down due to error: ", e);
+            close();
         }
     }
 
-    @UiThread
-    private void onStartCaptureTimeout() {
-        endCapture();
-    }
 
-    @WorkerThread // IScrollCaptureConnection
+    @BinderThread
     @Override
-    public void requestImage(Rect requestRect) {
+    public ICancellationSignal requestImage(Rect requestRect) throws RemoteException {
+        Trace.beginSection("requestImage");
         checkConnected();
         checkStarted();
-        scheduleTimeout(mTimeoutMillis, this::onRequestImageTimeout);
-        // Response is dispatched via ScrollCaptureSession, to onRequestImageCompleted
-        mHandler.post(() -> mSelectedTarget.getCallback().onScrollCaptureImageRequest(
-                mSession, new Rect(requestRect)));
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+
+        Consumer<Rect> listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onImageRequestCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureImageRequest(
+                mSession, mCancellation, new Rect(requestRect), listener));
+        Trace.endSection();
+        return cancellation;
     }
 
     @UiThread
-    void onRequestImageCompleted(long frameNumber, Rect capturedArea) {
-        final Rect finalCapturedArea = new Rect(capturedArea);
-        if (cancelTimeout()) {
-            mHandler.post(() -> {
-                try {
-                    mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea);
-                } catch (RemoteException e) {
-                    doShutdown();
-                }
-            });
-        }
-    }
-
-    @UiThread
-    private void onRequestImageTimeout() {
-        endCapture();
-    }
-
-    @WorkerThread // IScrollCaptureConnection
-    @Override
-    public void endCapture() {
-        if (isStarted()) {
-            scheduleTimeout(mTimeoutMillis, this::onEndCaptureTimeout);
-            mHandler.post(() ->
-                    mSelectedTarget.getCallback().onScrollCaptureEnd(this::onEndCaptureCompleted));
-        } else {
-            disconnect();
-        }
-    }
-
-    private boolean isStarted() {
-        return mCallbacks != null && mSelectedTarget != null;
-    }
-
-    @UiThread
-    private void onEndCaptureCompleted() { // onEndCaptureCompleted
-        if (cancelTimeout()) {
-            doShutdown();
-        }
-    }
-
-    @UiThread
-    private void onEndCaptureTimeout() {
-        doShutdown();
-    }
-
-
-    private void doShutdown() {
+    void onImageRequestCompleted(Rect capturedArea) {
         try {
-            if (mCallbacks != null) {
-                mCallbacks.onConnectionClosed();
-            }
+            mRemote.onImageRequestCompleted(0, capturedArea);
         } catch (RemoteException e) {
-            // Ignore
-        } finally {
-            disconnect();
+            Log.w(TAG, "Shutting down due to error: ", e);
+            close();
         }
     }
 
+    @BinderThread
+    @Override
+    public ICancellationSignal endCapture() throws RemoteException {
+        checkConnected();
+        checkStarted();
+
+        ICancellationSignal cancellation = CancellationSignal.createTransport();
+        mCancellation = CancellationSignal.fromTransport(cancellation);
+
+        Runnable listener =
+                SafeCallback.create(mCancellation, mUiThread, this::onEndCaptureCompleted);
+        // -> UiThread
+        mUiThread.execute(() -> mLocal.onScrollCaptureEnd(listener));
+        return cancellation;
+    }
+
+    @UiThread
+    private void onEndCaptureCompleted() {
+        synchronized (mLock) {
+            mStarted = false;
+            try {
+                mRemote.onCaptureEnded();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Shutting down due to error: ", e);
+                close();
+            }
+        }
+    }
+
+    @BinderThread
+    @Override
+    public void close() {
+        if (mStarted) {
+            Log.w(TAG, "close(): capture is still started?! Ending now.");
+
+            // -> UiThread
+            mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ }));
+            mStarted = false;
+        }
+        disconnect();
+    }
+
     /**
      * Shuts down this client and releases references to dependent objects. No attempt is made
      * to notify the controller, use with caution!
      */
-    public void disconnect() {
-        if (mSession != null) {
-            mSession.disconnect();
+    private void disconnect() {
+        synchronized (mLock) {
             mSession = null;
+            mConnected = false;
+            mStarted = false;
+            mRemote = null;
+            mLocal = null;
+            mCloseGuard.close();
         }
+    }
 
-        mSelectedTarget = null;
-        mCallbacks = null;
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    public boolean isStarted() {
+        return mStarted;
+    }
+
+    private synchronized void checkConnected() throws RemoteException {
+        synchronized (mLock) {
+            if (!mConnected) {
+                throw new RemoteException(new IllegalStateException("Not connected"));
+            }
+        }
+    }
+
+    private void checkStarted() throws RemoteException {
+        synchronized (mLock) {
+            if (!mStarted) {
+                throw new RemoteException(new IllegalStateException("Not started!"));
+            }
+        }
     }
 
     /** @return a string representation of the state of this client */
     public String toString() {
         return "ScrollCaptureConnection{"
+                + "connected=" + mConnected
+                + ", started=" + mStarted
                 + ", session=" + mSession
-                + ", selectedTarget=" + mSelectedTarget
-                + ", clientCallbacks=" + mCallbacks
+                + ", remote=" + mRemote
+                + ", local=" + mLocal
                 + "}";
     }
 
-    private boolean cancelTimeout() {
-        if (mTimeoutAction != null) {
-            return mTimeoutAction.cancel();
-        }
-        return false;
-    }
-
-    private void scheduleTimeout(long timeoutMillis, Runnable action) {
-        if (mTimeoutAction != null) {
-            mTimeoutAction.cancel();
-        }
-        mTimeoutAction = new DelayedAction(mHandler, timeoutMillis, action);
-    }
-
-    /** @hide */
     @VisibleForTesting
-    public static class DelayedAction {
-        private final AtomicBoolean mCompleted = new AtomicBoolean();
-        private final Object mToken = new Object();
-        private final Handler mHandler;
-        private final Runnable mAction;
+    public CancellationSignal getCancellation() {
+        return mCancellation;
+    }
 
-        @VisibleForTesting
-        public DelayedAction(Handler handler, long timeoutMillis, Runnable action) {
-            mHandler = handler;
-            mAction = action;
-            mHandler.postDelayed(this::onTimeout, mToken, timeoutMillis);
-        }
-
-        private boolean onTimeout() {
-            if (mCompleted.compareAndSet(false, true)) {
-                mAction.run();
-                return true;
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
             }
-            return false;
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static class SafeCallback<T> {
+        private final CancellationSignal mSignal;
+        private final WeakReference<T> mTargetRef;
+        private final Executor mExecutor;
+        private boolean mExecuted;
+
+        protected SafeCallback(CancellationSignal signal, Executor executor, T target) {
+            mSignal = signal;
+            mTargetRef = new WeakReference<>(target);
+            mExecutor = executor;
         }
 
-        /**
-         * Cause the timeout action to run immediately and mark as timed out.
-         *
-         * @return true if the timeout was run, false if the timeout had already been canceled
-         */
-        @VisibleForTesting
-        public boolean timeoutNow() {
-            return onTimeout();
-        }
-
-        /**
-         * Attempt to cancel the timeout action (such as after a callback is made)
-         *
-         * @return true if the timeout was canceled and will not run, false if time has expired and
-         * the timeout action has or will run momentarily
-         */
-        public boolean cancel() {
-            if (!mCompleted.compareAndSet(false, true)) {
-                // Whoops, too late!
-                return false;
+        // Provide the target to the consumer to invoke, forward on handler thread ONCE,
+        // and only if noy cancelled, and the target is still available (not collected)
+        protected final void maybeAccept(Consumer<T> targetConsumer) {
+            if (mExecuted) {
+                return;
             }
-            mHandler.removeCallbacksAndMessages(mToken);
-            return true;
+            mExecuted = true;
+            if (mSignal.isCanceled()) {
+                return;
+            }
+            T target = mTargetRef.get();
+            if (target == null) {
+                return;
+            }
+            mExecutor.execute(() -> targetConsumer.accept(target));
+        }
+
+        static Runnable create(CancellationSignal signal, Executor executor, Runnable target) {
+            return new RunnableCallback(signal, executor, target);
+        }
+
+        static <T> Consumer<T> create(CancellationSignal signal, Executor executor,
+                Consumer<T> target) {
+            return new ConsumerCallback<T>(signal, executor, target);
+        }
+    }
+
+    private static final class RunnableCallback extends SafeCallback<Runnable> implements Runnable {
+        RunnableCallback(CancellationSignal signal, Executor executor, Runnable target) {
+            super(signal, executor, target);
+        }
+
+        @Override
+        public void run() {
+            maybeAccept(Runnable::run);
+        }
+    }
+
+    private static final class ConsumerCallback<T> extends SafeCallback<Consumer<T>>
+            implements Consumer<T> {
+        ConsumerCallback(CancellationSignal signal, Executor executor, Consumer<T> target) {
+            super(signal, executor, target);
+        }
+
+        @Override
+        public void accept(T value) {
+            maybeAccept((target) -> target.accept(value));
         }
     }
 }
diff --git a/core/java/android/view/ScrollCaptureResponse.aidl b/core/java/android/view/ScrollCaptureResponse.aidl
new file mode 100644
index 0000000..3de2b80
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureResponse.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+parcelable ScrollCaptureResponse;
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
new file mode 100644
index 0000000..564113e
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.ArrayList;
+
+/** @hide */
+@DataClass(genToString = true, genGetters = true)
+public class ScrollCaptureResponse implements Parcelable {
+
+    /** Developer-facing human readable description of the result. */
+    @NonNull
+    private String mDescription = "";
+
+    // Remaining fields are non-null when isConnected() == true
+
+    /** The active connection for a successful result. */
+    @Nullable
+    @DataClass.MaySetToNull
+    private IScrollCaptureConnection mConnection = null;
+
+    /** The bounds of the window within the display */
+    @Nullable
+    private Rect mWindowBounds = null;
+
+    /** The bounds of the scrolling content, in window space. */
+    @Nullable
+    private Rect mBoundsInWindow = null;
+
+    /** The current window title. */
+    @Nullable
+    private String mWindowTitle = null;
+
+    /** Carries additional logging and debugging information when enabled. */
+    @NonNull
+    @DataClass.PluralOf("message")
+    private ArrayList<String> mMessages = new ArrayList<>();
+
+    /** Whether a connection has been returned. */
+    public boolean isConnected() {
+        return mConnection != null;
+    }
+
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/ScrollCaptureResponse.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    /* package-private */ ScrollCaptureResponse(
+            @NonNull String description,
+            @Nullable IScrollCaptureConnection connection,
+            @Nullable Rect windowBounds,
+            @Nullable Rect boundsInWindow,
+            @Nullable String windowTitle,
+            @NonNull ArrayList<String> messages) {
+        this.mDescription = description;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDescription);
+        this.mConnection = connection;
+        this.mWindowBounds = windowBounds;
+        this.mBoundsInWindow = boundsInWindow;
+        this.mWindowTitle = windowTitle;
+        this.mMessages = messages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Developer-facing human readable description of the result.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getDescription() {
+        return mDescription;
+    }
+
+    /**
+     * The active connection for a successful result.
+     */
+    @DataClass.Generated.Member
+    public @Nullable IScrollCaptureConnection getConnection() {
+        return mConnection;
+    }
+
+    /**
+     * The bounds of the window within the display
+     */
+    @DataClass.Generated.Member
+    public @Nullable Rect getWindowBounds() {
+        return mWindowBounds;
+    }
+
+    /**
+     * The bounds of the scrolling content, in window space.
+     */
+    @DataClass.Generated.Member
+    public @Nullable Rect getBoundsInWindow() {
+        return mBoundsInWindow;
+    }
+
+    /**
+     * The current window title.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getWindowTitle() {
+        return mWindowTitle;
+    }
+
+    /**
+     * Carries additional logging and debugging information when enabled.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ArrayList<String> getMessages() {
+        return mMessages;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ScrollCaptureResponse { " +
+                "description = " + mDescription + ", " +
+                "connection = " + mConnection + ", " +
+                "windowBounds = " + mWindowBounds + ", " +
+                "boundsInWindow = " + mBoundsInWindow + ", " +
+                "windowTitle = " + mWindowTitle + ", " +
+                "messages = " + mMessages +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mConnection != null) flg |= 0x2;
+        if (mWindowBounds != null) flg |= 0x4;
+        if (mBoundsInWindow != null) flg |= 0x8;
+        if (mWindowTitle != null) flg |= 0x10;
+        dest.writeByte(flg);
+        dest.writeString(mDescription);
+        if (mConnection != null) dest.writeStrongInterface(mConnection);
+        if (mWindowBounds != null) dest.writeTypedObject(mWindowBounds, flags);
+        if (mBoundsInWindow != null) dest.writeTypedObject(mBoundsInWindow, flags);
+        if (mWindowTitle != null) dest.writeString(mWindowTitle);
+        dest.writeStringList(mMessages);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    protected ScrollCaptureResponse(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String description = in.readString();
+        IScrollCaptureConnection connection = (flg & 0x2) == 0 ? null : IScrollCaptureConnection.Stub.asInterface(in.readStrongBinder());
+        Rect windowBounds = (flg & 0x4) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+        Rect boundsInWindow = (flg & 0x8) == 0 ? null : (Rect) in.readTypedObject(Rect.CREATOR);
+        String windowTitle = (flg & 0x10) == 0 ? null : in.readString();
+        ArrayList<String> messages = new ArrayList<>();
+        in.readStringList(messages);
+
+        this.mDescription = description;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mDescription);
+        this.mConnection = connection;
+        this.mWindowBounds = windowBounds;
+        this.mBoundsInWindow = boundsInWindow;
+        this.mWindowTitle = windowTitle;
+        this.mMessages = messages;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessages);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ScrollCaptureResponse> CREATOR
+            = new Parcelable.Creator<ScrollCaptureResponse>() {
+        @Override
+        public ScrollCaptureResponse[] newArray(int size) {
+            return new ScrollCaptureResponse[size];
+        }
+
+        @Override
+        public ScrollCaptureResponse createFromParcel(@NonNull android.os.Parcel in) {
+            return new ScrollCaptureResponse(in);
+        }
+    };
+
+    /**
+     * A builder for {@link ScrollCaptureResponse}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static class Builder {
+
+        private @NonNull String mDescription;
+        private @Nullable IScrollCaptureConnection mConnection;
+        private @Nullable Rect mWindowBounds;
+        private @Nullable Rect mBoundsInWindow;
+        private @Nullable String mWindowTitle;
+        private @NonNull ArrayList<String> mMessages;
+
+        private long mBuilderFieldsSet = 0L;
+
+        public Builder() {
+        }
+
+        /**
+         * Developer-facing human readable description of the result.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setDescription(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mDescription = value;
+            return this;
+        }
+
+        /**
+         * The active connection for a successful result.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setConnection(@Nullable IScrollCaptureConnection value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mConnection = value;
+            return this;
+        }
+
+        /**
+         * The bounds of the window within the display
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setWindowBounds(@NonNull Rect value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mWindowBounds = value;
+            return this;
+        }
+
+        /**
+         * The bounds of the scrolling content, in window space.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setBoundsInWindow(@NonNull Rect value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mBoundsInWindow = value;
+            return this;
+        }
+
+        /**
+         * The current window title.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setWindowTitle(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mWindowTitle = value;
+            return this;
+        }
+
+        /**
+         * Carries additional logging and debugging information when enabled.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setMessages(@NonNull ArrayList<String> value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mMessages = value;
+            return this;
+        }
+
+        /** @see #setMessages */
+        @DataClass.Generated.Member
+        public @NonNull Builder addMessage(@NonNull String value) {
+            if (mMessages == null) setMessages(new ArrayList<>());
+            mMessages.add(value);
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull ScrollCaptureResponse build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x1) == 0) {
+                mDescription = "";
+            }
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mConnection = null;
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mWindowBounds = null;
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mBoundsInWindow = null;
+            }
+            if ((mBuilderFieldsSet & 0x10) == 0) {
+                mWindowTitle = null;
+            }
+            if ((mBuilderFieldsSet & 0x20) == 0) {
+                mMessages = new ArrayList<>();
+            }
+            ScrollCaptureResponse o = new ScrollCaptureResponse(
+                    mDescription,
+                    mConnection,
+                    mWindowBounds,
+                    mBoundsInWindow,
+                    mWindowTitle,
+                    mMessages);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x40) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1612282689462L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic  boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/view/ScrollCaptureSearchResults.java b/core/java/android/view/ScrollCaptureSearchResults.java
new file mode 100644
index 0000000..3469b9d
--- /dev/null
+++ b/core/java/android/view/ScrollCaptureSearchResults.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.UiThread;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Collects nodes in the view hierarchy which have been identified as scrollable content.
+ *
+ * @hide
+ */
+@UiThread
+public final class ScrollCaptureSearchResults {
+    private final Executor mExecutor;
+    private final List<ScrollCaptureTarget> mTargets;
+    private final CancellationSignal mCancel;
+
+    private Runnable mOnCompleteListener;
+    private int mCompleted;
+    private boolean mComplete = true;
+
+    public ScrollCaptureSearchResults(Executor executor) {
+        mExecutor = executor;
+        mTargets = new ArrayList<>();
+        mCancel = new CancellationSignal();
+    }
+
+    // Public
+
+    /**
+     * Add the given target to the results.
+     *
+     * @param target the target to consider
+     */
+    public void addTarget(@NonNull ScrollCaptureTarget target) {
+        requireNonNull(target);
+
+        mTargets.add(target);
+        mComplete = false;
+        final ScrollCaptureCallback callback = target.getCallback();
+        final Consumer<Rect> consumer = new SearchRequest(target);
+
+        // Defer so the view hierarchy scan completes first
+        mExecutor.execute(
+                () -> callback.onScrollCaptureSearch(mCancel, consumer));
+    }
+
+    public boolean isComplete() {
+        return mComplete;
+    }
+
+    /**
+     * Provides a callback to be invoked as soon as all responses have been received from all
+     * targets to this point.
+     *
+     * @param onComplete listener to add
+     */
+    public void setOnCompleteListener(Runnable onComplete) {
+        if (mComplete) {
+            onComplete.run();
+        } else {
+            mOnCompleteListener = onComplete;
+        }
+    }
+
+    /**
+     * Indicates whether the search results are empty.
+     *
+     * @return true if no targets have been added
+     */
+    public boolean isEmpty() {
+        return mTargets.isEmpty();
+    }
+
+    /**
+     * Force the results to complete now, cancelling any pending requests and calling a complete
+     * listener if provided.
+     */
+    public void finish() {
+        if (!mComplete) {
+            mCancel.cancel();
+            signalComplete();
+        }
+    }
+
+    private void signalComplete() {
+        mComplete = true;
+        mTargets.sort(PRIORITY_ORDER);
+        if (mOnCompleteListener != null) {
+            mOnCompleteListener.run();
+            mOnCompleteListener = null;
+        }
+    }
+
+    @VisibleForTesting
+    public List<ScrollCaptureTarget> getTargets() {
+        return new ArrayList<>(mTargets);
+    }
+
+    /**
+     * Get the top ranked result out of all completed requests.
+     *
+     * @return the top ranked result
+     */
+    public ScrollCaptureTarget getTopResult() {
+        ScrollCaptureTarget target = mTargets.isEmpty() ? null : mTargets.get(0);
+        return target != null && target.getScrollBounds() != null ? target : null;
+    }
+
+    private class SearchRequest implements Consumer<Rect> {
+        private ScrollCaptureTarget mTarget;
+
+        SearchRequest(ScrollCaptureTarget target) {
+            mTarget = target;
+        }
+
+        @Override
+        public void accept(Rect scrollBounds) {
+            if (mTarget == null || mCancel.isCanceled()) {
+                return;
+            }
+            mExecutor.execute(() -> consume(scrollBounds));
+        }
+
+        private void consume(Rect scrollBounds) {
+            if (mTarget == null || mCancel.isCanceled()) {
+                return;
+            }
+            if (!nullOrEmpty(scrollBounds)) {
+                mTarget.setScrollBounds(scrollBounds);
+                mTarget.updatePositionInWindow();
+            }
+            mCompleted++;
+            mTarget = null;
+
+            // All done?
+            if (mCompleted == mTargets.size()) {
+                signalComplete();
+            }
+        }
+    }
+
+    private static final int AFTER = 1;
+    private static final int BEFORE = -1;
+    private static final int EQUAL = 0;
+
+    static final Comparator<ScrollCaptureTarget> PRIORITY_ORDER = (a, b) -> {
+        if (a == null && b == null) {
+            return 0;
+        } else if (a == null || b == null) {
+            return (a == null) ? 1 : -1;
+        }
+
+        boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds());
+        boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds());
+        if (emptyScrollBoundsA || emptyScrollBoundsB) {
+            if (emptyScrollBoundsA && emptyScrollBoundsB) {
+                return EQUAL;
+            }
+            // Prefer the one with a non-empty scroll bounds
+            if (emptyScrollBoundsA) {
+                return AFTER;
+            }
+            return BEFORE;
+        }
+
+        final View viewA = a.getContainingView();
+        final View viewB = b.getContainingView();
+
+        // Prefer any view with scrollCaptureHint="INCLUDE", over one without
+        // This is an escape hatch for the next rule (descendants first)
+        boolean hintIncludeA = hasIncludeHint(viewA);
+        boolean hintIncludeB = hasIncludeHint(viewB);
+        if (hintIncludeA != hintIncludeB) {
+            return (hintIncludeA) ? BEFORE : AFTER;
+        }
+        // If the views are relatives, prefer the descendant. This allows implementations to
+        // leverage nested scrolling APIs by interacting with the innermost scrollable view (as
+        // would happen with touch input).
+        if (isDescendant(viewA, viewB)) {
+            return BEFORE;
+        }
+        if (isDescendant(viewB, viewA)) {
+            return AFTER;
+        }
+
+        // finally, prefer one with larger scroll bounds
+        int scrollAreaA = area(a.getScrollBounds());
+        int scrollAreaB = area(b.getScrollBounds());
+        return (scrollAreaA >= scrollAreaB) ? BEFORE : AFTER;
+    };
+
+    private static int area(Rect r) {
+        return r.width() * r.height();
+    }
+
+    private static boolean nullOrEmpty(Rect r) {
+        return r == null || r.isEmpty();
+    }
+
+    private static boolean hasIncludeHint(View view) {
+        return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0;
+    }
+
+    /**
+     * Determines if {@code otherView} is a descendant of {@code view}.
+     *
+     * @param view      a view
+     * @param otherView another view
+     * @return true if {@code view} is an ancestor of {@code otherView}
+     */
+    private static boolean isDescendant(@NonNull View view, @NonNull View otherView) {
+        if (view == otherView) {
+            return false;
+        }
+        ViewParent otherParent = otherView.getParent();
+        while (otherParent != view && otherParent != null) {
+            otherParent = otherParent.getParent();
+        }
+        return otherParent == view;
+    }
+
+    void dump(IndentingPrintWriter writer) {
+        writer.println("results:");
+        writer.increaseIndent();
+        writer.println("complete: " + isComplete());
+        writer.println("cancelled: " + mCancel.isCanceled());
+        writer.println("targets:");
+        writer.increaseIndent();
+        if (isEmpty()) {
+            writer.println("None");
+        } else {
+            for (int i = 0; i < mTargets.size(); i++) {
+                writer.println("[" + i + "]");
+                writer.increaseIndent();
+                mTargets.get(i).dump(writer);
+                writer.decreaseIndent();
+            }
+            writer.decreaseIndent();
+        }
+        writer.decreaseIndent();
+    }
+}
diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java
index 92617a3..748e7ea 100644
--- a/core/java/android/view/ScrollCaptureSession.java
+++ b/core/java/android/view/ScrollCaptureSession.java
@@ -16,18 +16,15 @@
 
 package android.view;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.graphics.Point;
 import android.graphics.Rect;
 
 /**
  * A session represents the scope of interaction between a {@link ScrollCaptureCallback} and the
- * system during an active scroll capture operation. During the scope of a session, a callback
- * will receive a series of requests for image data. Resources provided here are valid for use
- * until {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable)}.
- *
- * @hide
+ * system during an active scroll capture operation.
  */
 public class ScrollCaptureSession {
 
@@ -35,22 +32,27 @@
     private final Rect mScrollBounds;
     private final Point mPositionInWindow;
 
-    @Nullable
-    private ScrollCaptureConnection mConnection;
-
-    /** @hide */
-    public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow,
-            ScrollCaptureConnection connection) {
-        mSurface = surface;
-        mScrollBounds = scrollBounds;
-        mPositionInWindow = positionInWindow;
-        mConnection = connection;
+    /**
+     * Constructs a new session instance.
+     *
+     * @param surface the surface to consume generated images
+     * @param scrollBounds the bounds of the capture area within the containing view
+     * @param positionInWindow the offset of scrollBounds within the window
+     */
+    public ScrollCaptureSession(@NonNull Surface surface, @NonNull Rect scrollBounds,
+            @NonNull Point positionInWindow) {
+        mSurface = requireNonNull(surface);
+        mScrollBounds = requireNonNull(scrollBounds);
+        mPositionInWindow = requireNonNull(positionInWindow);
     }
 
     /**
      * Returns a
      * <a href="https://source.android.com/devices/graphics/arch-bq-gralloc">BufferQueue</a> in the
      * form of a {@link Surface} for transfer of image buffers.
+     * <p>
+     * The surface is guaranteed to remain {@link Surface#isValid() valid} until the session
+     * {@link ScrollCaptureCallback#onScrollCaptureEnd(Runnable) ends}.
      *
      * @return the surface for transferring image buffers
      * @throws IllegalStateException if the session has been closed
@@ -80,26 +82,4 @@
     public Point getPositionInWindow() {
         return mPositionInWindow;
     }
-
-    /**
-     * Notify the system that an a buffer has been posted via the getSurface() channel.
-     *
-     * @param frameNumber  the frame number of the queued buffer
-     * @param capturedArea the area captured, relative to scroll bounds
-     */
-    public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) {
-        if (mConnection != null) {
-            mConnection.onRequestImageCompleted(frameNumber, capturedArea);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void disconnect() {
-        mConnection = null;
-        if (mSurface.isValid()) {
-            mSurface.release();
-        }
-    }
 }
diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java
index f3fcabb..4fd4889 100644
--- a/core/java/android/view/ScrollCaptureTarget.java
+++ b/core/java/android/view/ScrollCaptureTarget.java
@@ -22,14 +22,16 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 
 import com.android.internal.util.FastMath;
 
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
 /**
  * A target collects the set of contextual information for a ScrollCaptureHandler discovered during
  * a {@link View#dispatchScrollCaptureSearch scroll capture search}.
- *
- * @hide
  */
 public final class ScrollCaptureTarget {
     private final View mContainingView;
@@ -41,7 +43,6 @@
 
     private final float[] mTmpFloatArr = new float[2];
     private final Matrix mMatrixViewLocalToWindow = new Matrix();
-    private final Rect mTmpRect = new Rect();
 
     public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect,
             @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) {
@@ -52,7 +53,10 @@
         mPositionInWindow = positionInWindow;
     }
 
-    /** @return the hint that the {@code containing view} had during the scroll capture search */
+    /**
+     * @return the hint that the {@code containing view} had during the scroll capture search
+     * @see View#getScrollCaptureHint()
+     */
     @View.ScrollCaptureHint
     public int getHint() {
         return mHint;
@@ -71,8 +75,7 @@
     }
 
     /**
-     * Returns the un-clipped, visible bounds of the containing view during the scroll capture
-     * search. This is used to determine on-screen area to assist in selecting the primary target.
+     * Returns the visible bounds of the containing view.
      *
      * @return the visible bounds of the {@code containing view} in view-local coordinates
      */
@@ -81,13 +84,17 @@
         return mLocalVisibleRect;
     }
 
-    /** @return the position of the {@code containing view} within the window */
+    /** @return the position of the visible bounds of the containing view within the window */
     @NonNull
     public Point getPositionInWindow() {
         return mPositionInWindow;
     }
 
-    /** @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback} */
+    /**
+     * @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback}
+     *
+     * @see ScrollCaptureCallback#onScrollCaptureSearch(CancellationSignal, Consumer)
+     */
     @Nullable
     public Rect getScrollBounds() {
         return mScrollBounds;
@@ -119,8 +126,8 @@
     }
 
     /**
-     * Refresh the value of {@link #mLocalVisibleRect} and {@link #mPositionInWindow} based on the
-     * current state of the {@code containing view}.
+     * Refresh the local visible bounds and it's offset within the window, based on the current
+     * state of the {@code containing view}.
      */
     @UiThread
     public void updatePositionInWindow() {
@@ -132,4 +139,27 @@
         roundIntoPoint(mPositionInWindow, mTmpFloatArr);
     }
 
+    public String toString() {
+        return "ScrollCaptureTarget{" + "view=" + mContainingView
+                + ", callback=" + mCallback
+                + ", scrollBounds=" + mScrollBounds
+                + ", localVisibleRect=" + mLocalVisibleRect
+                + ", positionInWindow=" + mPositionInWindow
+                + "}";
+    }
+
+    void dump(@NonNull PrintWriter writer) {
+        View view = getContainingView();
+        writer.println("view: " + view);
+        writer.println("hint: " + mHint);
+        writer.println("callback: " + mCallback);
+        writer.println("scrollBounds: "
+                + (mScrollBounds == null ? "null" : mScrollBounds.toShortString()));
+        Point inWindow = getPositionInWindow();
+        writer.println("positionInWindow: "
+                + ((inWindow == null) ? "null" : "[" + inWindow.x + "," + inWindow.y + "]"));
+        Rect localVisible = getLocalVisibleRect();
+        writer.println("localVisibleRect: "
+                + (localVisible == null ? "null" : localVisible.toShortString()));
+    }
 }
diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java
deleted file mode 100644
index e4316bb..0000000
--- a/core/java/android/view/ScrollCaptureTargetResolver.java
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.AnyThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UiThread;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-
-import java.util.Queue;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
-/**
- * Queries additional state from a list of {@link ScrollCaptureTarget targets} via asynchronous
- * callbacks, then aggregates and reduces the target list to a single target, or null if no target
- * is suitable.
- * <p>
- * The rules for selection are (in order):
- * <ul>
- * <li>prefer getScrollBounds(): non-empty
- * <li>prefer View.getScrollCaptureHint == SCROLL_CAPTURE_HINT_INCLUDE
- * <li>prefer descendants before parents
- * <li>prefer larger area for getScrollBounds() (clipped to view bounds)
- * </ul>
- *
- * <p>
- * All calls to {@link ScrollCaptureCallback#onScrollCaptureSearch} are made on the main thread,
- * with results are queued and consumed to the main thread as well.
- *
- * @see #start(Handler, long, Consumer)
- *
- * @hide
- */
-@UiThread
-public class ScrollCaptureTargetResolver {
-    private static final String TAG = "ScrollCaptureTargetRes";
-
-    private final Object mLock = new Object();
-
-    private final Queue<ScrollCaptureTarget> mTargets;
-    private Handler mHandler;
-    private long mTimeLimitMillis;
-
-    private Consumer<ScrollCaptureTarget> mWhenComplete;
-    private int mPendingBoundsRequests;
-    private long mDeadlineMillis;
-
-    private ScrollCaptureTarget mResult;
-    private boolean mFinished;
-
-    private boolean mStarted;
-
-    private static int area(Rect r) {
-        return r.width() * r.height();
-    }
-
-    private static boolean nullOrEmpty(Rect r) {
-        return r == null || r.isEmpty();
-    }
-
-    /**
-     * Binary operator which selects the best {@link ScrollCaptureTarget}.
-     */
-    private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) {
-        if (a == null && b == null) {
-            return null;
-        } else if (a == null || b == null) {
-            ScrollCaptureTarget c = (a == null) ? b : a;
-            return c;
-        }
-
-        boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds());
-        boolean emptyScrollBoundsB = nullOrEmpty(b.getScrollBounds());
-        if (emptyScrollBoundsA || emptyScrollBoundsB) {
-            if (emptyScrollBoundsA && emptyScrollBoundsB) {
-                // Both have an empty or null scrollBounds
-                Log.d(TAG, "chooseTarget: (both have empty or null bounds) return " + null);
-                return null;
-            }
-            // Prefer the one with a non-empty scroll bounds
-            if (emptyScrollBoundsA) {
-                Log.d(TAG, "chooseTarget: (a has empty or null bounds) return " + b);
-                return b;
-            }
-            Log.d(TAG, "chooseTarget: (b has empty or null bounds) return " + a);
-            return a;
-        }
-
-        final View viewA = a.getContainingView();
-        final View viewB = b.getContainingView();
-
-        // Prefer any view with scrollCaptureHint="INCLUDE", over one without
-        // This is an escape hatch for the next rule (descendants first)
-        boolean hintIncludeA = hasIncludeHint(viewA);
-        boolean hintIncludeB = hasIncludeHint(viewB);
-        if (hintIncludeA != hintIncludeB) {
-            ScrollCaptureTarget c = (hintIncludeA) ? a : b;
-            Log.d(TAG, "chooseTarget: (has hint=INCLUDE) return " + c);
-            return c;
-        }
-
-        // If the views are relatives, prefer the descendant. This allows implementations to
-        // leverage nested scrolling APIs by interacting with the innermost scrollable view (as
-        // would happen with touch input).
-        if (isDescendant(viewA, viewB)) {
-            Log.d(TAG, "chooseTarget: (b is descendant of a) return " + b);
-            return b;
-        }
-        if (isDescendant(viewB, viewA)) {
-            Log.d(TAG, "chooseTarget: (a is descendant of b) return " + a);
-            return a;
-        }
-
-        // finally, prefer one with larger scroll bounds
-        int scrollAreaA = area(a.getScrollBounds());
-        int scrollAreaB = area(b.getScrollBounds());
-        ScrollCaptureTarget c = (scrollAreaA >= scrollAreaB) ? a : b;
-        Log.d(TAG, "chooseTarget: return " + c);
-        return c;
-    }
-
-    /**
-     * Creates an instance to query and filter {@code target}.
-     *
-     * @param targets   a list of {@link ScrollCaptureTarget} as collected by {@link
-     *                  View#dispatchScrollCaptureSearch}.
-     * @see #start(Handler, long, Consumer)
-     */
-    public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) {
-        mTargets = targets;
-    }
-
-    void checkThread() {
-        if (mHandler.getLooper() != Looper.myLooper()) {
-            throw new IllegalStateException("Called from wrong thread! ("
-                    + Thread.currentThread().getName() + ")");
-        }
-    }
-
-    /**
-     * Blocks until a result is returned (after completion or timeout).
-     * <p>
-     * For testing only. Normal usage should receive a callback after calling {@link #start}.
-     */
-    @VisibleForTesting
-    public ScrollCaptureTarget waitForResult() throws InterruptedException {
-        synchronized (mLock) {
-            while (!mFinished) {
-                mLock.wait();
-            }
-        }
-        return mResult;
-    }
-
-    private void supplyResult(ScrollCaptureTarget target) {
-        checkThread();
-        if (mFinished) {
-            return;
-        }
-        mResult = chooseTarget(mResult, target);
-        boolean finish = mPendingBoundsRequests == 0
-                || SystemClock.uptimeMillis() >= mDeadlineMillis;
-        if (finish) {
-            mPendingBoundsRequests = 0;
-            mWhenComplete.accept(mResult);
-            synchronized (mLock) {
-                mFinished = true;
-                mLock.notify();
-            }
-            mWhenComplete = null;
-        }
-    }
-
-    /**
-     * Asks all targets for {@link ScrollCaptureCallback#onScrollCaptureSearch(Consumer)
-     * scrollBounds}, and selects the primary target according to the {@link
-     * #chooseTarget} function.
-     *
-     * @param timeLimitMillis the amount of time to wait for all responses before delivering the top
-     *                        result
-     * @param resultConsumer  the consumer to receive the primary target
-     */
-    @AnyThread
-    public void start(Handler uiHandler, long timeLimitMillis,
-            Consumer<ScrollCaptureTarget> resultConsumer) {
-        synchronized (mLock) {
-            if (mStarted) {
-                throw new IllegalStateException("already started!");
-            }
-            if (timeLimitMillis < 0) {
-                throw new IllegalArgumentException("Time limit must be positive");
-            }
-            mHandler = uiHandler;
-            mTimeLimitMillis = timeLimitMillis;
-            mWhenComplete = resultConsumer;
-            if (mTargets.isEmpty()) {
-                mHandler.post(() -> supplyResult(null));
-                return;
-            }
-            mStarted = true;
-            uiHandler.post(this::run);
-        }
-    }
-
-    private void run() {
-        checkThread();
-
-        mPendingBoundsRequests = mTargets.size();
-        for (ScrollCaptureTarget target : mTargets) {
-            queryTarget(target);
-        }
-        mDeadlineMillis = SystemClock.uptimeMillis() + mTimeLimitMillis;
-        mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
-    }
-
-    private final Runnable mTimeoutRunnable = () -> {
-        checkThread();
-        supplyResult(null);
-    };
-
-    /**
-     * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch}
-     * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}.
-     *
-     * @param target the target to add
-     */
-    @UiThread
-    private void queryTarget(@NonNull ScrollCaptureTarget target) {
-        checkThread();
-        final ScrollCaptureCallback callback = target.getCallback();
-        // from the UI thread, request scroll bounds
-        callback.onScrollCaptureSearch(
-                // allow only one callback to onReady.accept():
-                new SingletonConsumer<Rect>(
-                        // Queue and consume on the UI thread
-                        ((scrollBounds) -> mHandler.post(
-                                () -> onScrollBoundsProvided(target, scrollBounds)))));
-    }
-
-    @UiThread
-    private void onScrollBoundsProvided(ScrollCaptureTarget target, @Nullable Rect scrollBounds) {
-        checkThread();
-        if (mFinished) {
-            return;
-        }
-
-        // Record progress.
-        mPendingBoundsRequests--;
-
-        // Remove the timeout.
-        mHandler.removeCallbacks(mTimeoutRunnable);
-
-        boolean doneOrTimedOut = mPendingBoundsRequests == 0
-                || SystemClock.uptimeMillis() >= mDeadlineMillis;
-
-        final View containingView = target.getContainingView();
-        if (!nullOrEmpty(scrollBounds) && containingView.isAggregatedVisible()) {
-            target.updatePositionInWindow();
-            target.setScrollBounds(scrollBounds);
-            supplyResult(target);
-        }
-
-        if (!mFinished) {
-            // Reschedule the timeout.
-            mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis);
-        }
-    }
-
-    private static boolean hasIncludeHint(View view) {
-        return (view.getScrollCaptureHint() & View.SCROLL_CAPTURE_HINT_INCLUDE) != 0;
-    }
-
-    /**
-     * Determines if {@code otherView} is a descendant of {@code view}.
-     *
-     * @param view      a view
-     * @param otherView another view
-     * @return true if {@code view} is an ancestor of {@code otherView}
-     */
-    private static boolean isDescendant(@NonNull View view, @NonNull View otherView) {
-        if (view == otherView) {
-            return false;
-        }
-        ViewParent otherParent = otherView.getParent();
-        while (otherParent != view && otherParent != null) {
-            otherParent = otherParent.getParent();
-        }
-        return otherParent == view;
-    }
-
-    /**
-     * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures
-     * that the receiver of the consumer does not retain a reference to {@code target} after use nor
-     * cause race conditions by invoking {@link Consumer#accept accept} more than once.
-     */
-    static class SingletonConsumer<T> implements Consumer<T> {
-        final AtomicReference<Consumer<T>> mAtomicRef;
-
-        /**
-         * @param target the target consumer
-         **/
-        SingletonConsumer(Consumer<T> target) {
-            mAtomicRef = new AtomicReference<>(target);
-        }
-
-        @Override
-        public void accept(T t) {
-            final Consumer<T> consumer = mAtomicRef.getAndSet(null);
-            if (consumer != null) {
-                consumer.accept(t);
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0832578..03dd100 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3504,64 +3504,4 @@
     public static Transaction getGlobalTransaction() {
         return sGlobalTransaction;
     }
-
-    /**
-     * Wrapper for sending blur data to SurfaceFlinger.
-     * @hide
-     */
-    public static final class BlurRegion {
-        public int blurRadius;
-        public float cornerRadiusTL;
-        public float cornerRadiusTR;
-        public float cornerRadiusBL;
-        public float cornerRadiusBR;
-        public float alpha = 1;
-        public boolean visible = true;
-        public final Rect rect = new Rect();
-
-        private final float[] mFloatArray = new float[10];
-
-        public BlurRegion() {
-        }
-
-        public BlurRegion(BlurRegion other) {
-            rect.set(other.rect);
-            blurRadius = other.blurRadius;
-            alpha = other.alpha;
-            cornerRadiusTL = other.cornerRadiusTL;
-            cornerRadiusTR = other.cornerRadiusTR;
-            cornerRadiusBL = other.cornerRadiusBL;
-            cornerRadiusBR = other.cornerRadiusBR;
-        }
-
-        /**
-         * Serializes this class into a float array that's more JNI friendly.
-         */
-        public float[] toFloatArray() {
-            mFloatArray[0] = blurRadius;
-            mFloatArray[1] = alpha;
-            mFloatArray[2] = rect.left;
-            mFloatArray[3] = rect.top;
-            mFloatArray[4] = rect.right;
-            mFloatArray[5] = rect.bottom;
-            mFloatArray[6] = cornerRadiusTL;
-            mFloatArray[7] = cornerRadiusTR;
-            mFloatArray[8] = cornerRadiusBL;
-            mFloatArray[9] = cornerRadiusBR;
-            return mFloatArray;
-        }
-
-        @Override
-        public String toString() {
-            return "BlurRegion{"
-                    + "blurRadius=" + blurRadius
-                    + ", corners={" + cornerRadiusTL
-                    + "," + cornerRadiusTR
-                    + "," + cornerRadiusBL
-                    + "," + cornerRadiusBR
-                    + "}, alpha=" + alpha
-                    + ", rect=" + rect
-                    + "}";
-        }
-    }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3789324..ebef464 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -184,10 +184,10 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Queue;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -1484,7 +1484,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_AUTO = 0;
 
@@ -1495,7 +1494,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_EXCLUDE = 0x1;
 
@@ -1506,7 +1504,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_INCLUDE = 0x2;
 
@@ -1517,7 +1514,6 @@
      *
      * @see #getScrollCaptureHint()
      * @see #setScrollCaptureHint(int)
-     * @hide
      */
     public static final int SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS = 0x4;
 
@@ -30053,8 +30049,6 @@
      * Returns the current scroll capture hint for this view.
      *
      * @return the current scroll capture hint
-     *
-     * @hide
      */
     @ScrollCaptureHint
     public int getScrollCaptureHint() {
@@ -30067,8 +30061,6 @@
      * scroll capture targets.
      *
      * @param hint the scrollCaptureHint flags value to set
-     *
-     * @hide
      */
     public void setScrollCaptureHint(@ScrollCaptureHint int hint) {
         mPrivateFlags4 &= ~PFLAG4_SCROLL_CAPTURE_HINT_MASK;
@@ -30088,10 +30080,8 @@
      * setting a custom callback to help ensure it is selected as the target.
      *
      * @param callback the new callback to assign
-     *
-     * @hide
      */
-    public void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) {
+    public final void setScrollCaptureCallback(@Nullable ScrollCaptureCallback callback) {
         getListenerInfo().mScrollCaptureCallback = callback;
     }
 
@@ -30110,29 +30100,54 @@
     }
 
     /**
+     * Dispatch a scroll capture search request down the view hierarchy.
+     *
+     * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to
+     *                         the parent
+     * @param windowOffset     the offset of this view within the window
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
+     */
+    public void dispatchScrollCaptureSearch(
+            @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
+            @NonNull Consumer<ScrollCaptureTarget> targets) {
+        onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+    }
+
+    /**
      * Called when scroll capture is requested, to search for appropriate content to scroll. If
      * applicable, this view adds itself to the provided list for consideration, subject to the
      * flags set by {@link #setScrollCaptureHint}.
      *
      * @param localVisibleRect the local visible rect of this view
      * @param windowOffset     the offset of localVisibleRect within the window
-     * @param targets          a queue which collects potential targets
-     *
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
      * @throws IllegalStateException if this view is not attached to a window
-     * @hide
      */
-    public void dispatchScrollCaptureSearch(@NonNull Rect localVisibleRect,
-            @NonNull Point windowOffset, @NonNull Queue<ScrollCaptureTarget> targets) {
+    public void onScrollCaptureSearch(@NonNull Rect localVisibleRect,
+            @NonNull Point windowOffset, @NonNull Consumer<ScrollCaptureTarget> targets) {
         int hint = getScrollCaptureHint();
         if ((hint & SCROLL_CAPTURE_HINT_EXCLUDE) != 0) {
             return;
         }
+        boolean rectIsVisible = true;
+
+        // Apply clipBounds if present.
+        if (mClipBounds != null) {
+            rectIsVisible = localVisibleRect.intersect(mClipBounds);
+        }
+        if (!rectIsVisible) {
+            return;
+        }
 
         // Get a callback provided by the framework, library or application.
         ScrollCaptureCallback callback =
                 (mListenerInfo == null) ? null : mListenerInfo.mScrollCaptureCallback;
 
-        // Try internal support for standard scrolling containers.
+        // Try framework support for standard scrolling containers.
         if (callback == null) {
             callback = createScrollCaptureCallbackInternal(localVisibleRect, windowOffset);
         }
@@ -30142,7 +30157,7 @@
             // Add to the list for consideration
             Point offset = new Point(windowOffset.x, windowOffset.y);
             Rect rect = new Rect(localVisibleRect);
-            targets.add(new ScrollCaptureTarget(this, rect, offset, callback));
+            targets.accept(new ScrollCaptureTarget(this, rect, offset, callback));
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 37bea58..38a5937 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -77,7 +77,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Queue;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -7463,92 +7463,73 @@
     }
 
     /**
-     * Offsets the given rectangle in parent's local coordinates into child's coordinate space
-     * and clips the result to the child View's bounds, padding and clipRect if appropriate. If the
-     * resulting rectangle is not empty, the request is forwarded to the child.
-     * <p>
-     * Note: This method does not account for any static View transformations which may be
-     * applied to the child view.
-     *
-     * @param child            the child to dispatch to
-     * @param localVisibleRect the visible (clipped) area of this ViewGroup, in local coordinates
-     * @param windowOffset     the offset of localVisibleRect within the window
-     * @param targets          a queue to collect located targets
-     */
-    private void dispatchTransformedScrollCaptureSearch(View child, Rect localVisibleRect,
-            Point windowOffset, Queue<ScrollCaptureTarget> targets) {
-
-        // copy local visible rect for modification and dispatch
-        final Rect childVisibleRect = getTempRect();
-        childVisibleRect.set(localVisibleRect);
-
-        // transform to child coords
-        final Point childWindowOffset = getTempPoint();
-        childWindowOffset.set(windowOffset.x, windowOffset.y);
-
-        final int dx = child.mLeft - mScrollX;
-        final int dy = child.mTop - mScrollY;
-
-        childVisibleRect.offset(-dx, -dy);
-        childWindowOffset.offset(dx, dy);
-
-        boolean rectIsVisible = true;
-        final int width = mRight - mLeft;
-        final int height = mBottom - mTop;
-
-        // Clip to child bounds
-        if (getClipChildren()) {
-            rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(), child.getHeight());
-        }
-
-        // Clip to child padding.
-        if (rectIsVisible && (child instanceof ViewGroup)
-                && ((ViewGroup) child).getClipToPadding()) {
-            rectIsVisible = childVisibleRect.intersect(
-                    child.mPaddingLeft, child.mPaddingTop,
-                    child.getWidth() - child.mPaddingRight,
-                    child.getHeight() - child.mPaddingBottom);
-        }
-        // Clip to child clipBounds.
-        if (rectIsVisible && child.mClipBounds != null) {
-            rectIsVisible = childVisibleRect.intersect(child.mClipBounds);
-        }
-        if (rectIsVisible) {
-            child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
-        }
-    }
-
-    /**
      * Handle the scroll capture search request by checking this view if applicable, then to each
      * child view.
      *
      * @param localVisibleRect the visible area of this ViewGroup in local coordinates, according to
      *                         the parent
      * @param windowOffset     the offset of this view within the window
-     * @param targets          the collected list of scroll capture targets
-     *
-     * @hide
+     * @param targets          accepts potential scroll capture targets; {@link Consumer#accept
+     *                         results.accept} may be called zero or more times on the calling
+     *                         thread before onScrollCaptureSearch returns
      */
     @Override
     public void dispatchScrollCaptureSearch(
             @NonNull Rect localVisibleRect, @NonNull Point windowOffset,
-            @NonNull Queue<ScrollCaptureTarget> targets) {
+            @NonNull Consumer<ScrollCaptureTarget> targets) {
+
+        // copy local visible rect for modification and dispatch
+        final Rect rect = getTempRect();
+        rect.set(localVisibleRect);
+
+        if (getClipToPadding()) {
+            rect.inset(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
+        }
 
         // Dispatch to self first.
         super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
 
-        // Then dispatch to children, if not excluding descendants.
-        if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) == 0) {
-            final int childCount = getChildCount();
-            for (int i = 0; i < childCount; i++) {
-                View child = getChildAt(i);
-                // Only visible views can be captured.
-                if (child.getVisibility() != View.VISIBLE) {
-                    continue;
-                }
-                // Transform to child coords and dispatch
-                dispatchTransformedScrollCaptureSearch(child, localVisibleRect, windowOffset,
-                        targets);
+        // Skip children if descendants excluded.
+        if ((getScrollCaptureHint() & SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS) != 0) {
+            return;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            // Only visible views can be captured.
+            if (child.getVisibility() != View.VISIBLE) {
+                continue;
+            }
+            // Offset the given rectangle (in parent's local coordinates) into child's coordinate
+            // space and clip the result to the child View's bounds, padding and clipRect as needed.
+            // If the resulting rectangle is not empty, the request is forwarded to the child.
+
+            // copy local visible rect for modification and dispatch
+            final Rect childVisibleRect = getTempRect();
+            childVisibleRect.set(localVisibleRect);
+
+            // transform to child coords
+            final Point childWindowOffset = getTempPoint();
+            childWindowOffset.set(windowOffset.x, windowOffset.y);
+
+            final int dx = child.mLeft - mScrollX;
+            final int dy = child.mTop - mScrollY;
+
+            childVisibleRect.offset(-dx, -dy);
+            childWindowOffset.offset(dx, dy);
+
+            boolean rectIsVisible = true;
+
+            // Clip to child bounds
+            if (getClipChildren()) {
+                rectIsVisible = childVisibleRect.intersect(0, 0, child.getWidth(),
+                        child.getHeight());
+            }
+
+            // Clip to child padding.
+            if (rectIsVisible) {
+                child.dispatchScrollCaptureSearch(childVisibleRect, childWindowOffset, targets);
             }
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1abcb15..f8e65bd 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -141,6 +141,7 @@
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.LongArray;
 import android.util.MergedConfiguration;
@@ -202,6 +203,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -274,6 +276,11 @@
      */
     private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2;
 
+    /**
+     * Maximum time to wait for {@link View#dispatchScrollCaptureSearch} to complete.
+     */
+    private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -668,8 +675,6 @@
     private final InsetsController mInsetsController;
     private final ImeFocusController mImeFocusController;
 
-    private ScrollCaptureConnection mScrollCaptureConnection;
-
     private boolean mIsSurfaceOpaque;
 
     private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =
@@ -683,12 +688,6 @@
         return mImeFocusController;
     }
 
-    /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */
-    @Nullable
-    public ScrollCaptureConnection getScrollCaptureConnection() {
-        return mScrollCaptureConnection;
-    }
-
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
 
     private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -730,6 +729,8 @@
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
 
+    private long mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
+
     /**
      * Increment this value when the surface has been replaced.
      */
@@ -817,6 +818,8 @@
         mImeFocusController = new ImeFocusController(this);
         AudioManager audioManager = mContext.getSystemService(AudioManager.class);
         mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
+
+        mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -3966,11 +3969,12 @@
     }
 
     private void addFrameCallbackIfNeeded() {
-        boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
-        boolean hasBlur = mBlurRegionAggregator.hasRegions();
-        boolean reportNextDraw = mReportNextDraw;
+        final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
+        final boolean reportNextDraw = mReportNextDraw;
+        final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
+        final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
 
-        if (!nextDrawUseBlastSync && !reportNextDraw && !hasBlur) {
+        if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) {
             return;
         }
 
@@ -3978,18 +3982,22 @@
             Log.d(mTag, "Creating frameDrawingCallback"
                     + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
                     + " reportNextDraw=" + reportNextDraw
-                    + " hasBlur=" + hasBlur);
+                    + " hasBlurUpdates=" + hasBlurUpdates);
         }
 
-        // The callback will run on a worker thread pool from the render thread.
+        final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
+                needsCallbackForBlur ?  mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
+
+        // The callback will run on the render thread.
         HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
                         + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
             }
 
-            if (hasBlur) {
-                mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame);
+            if (needsCallbackForBlur) {
+                mBlurRegionAggregator
+                    .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
             }
 
             if (mBlastBufferQueue == null) {
@@ -9223,9 +9231,9 @@
      * Collect and include any ScrollCaptureCallback instances registered with the window.
      *
      * @see #addScrollCaptureCallback(ScrollCaptureCallback)
-     * @param targets the search queue for targets
+     * @param results an object to collect the results of the search
      */
-    private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) {
+    private void collectRootScrollCaptureTargets(ScrollCaptureSearchResults results) {
         if (mRootScrollCaptureCallbacks == null) {
             return;
         }
@@ -9233,26 +9241,45 @@
             // Add to the list for consideration
             Point offset = new Point(mView.getLeft(), mView.getTop());
             Rect rect = new Rect(0, 0, mView.getWidth(), mView.getHeight());
-            targets.add(new ScrollCaptureTarget(mView, rect, offset, cb));
+            results.addTarget(new ScrollCaptureTarget(mView, rect, offset, cb));
         }
     }
 
     /**
-     * Handles an inbound request for scroll capture from the system. If a client is not already
-     * active, a search will be dispatched through the view tree to locate scrolling content.
+     * Update the timeout for scroll capture requests. Only affects this view root.
+     * The default value is {@link #SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS}.
+     *
+     * @param timeMillis the new timeout in milliseconds
+     */
+    public void setScrollCaptureRequestTimeout(int timeMillis) {
+        mScrollCaptureRequestTimeout = timeMillis;
+    }
+
+    /**
+     * Get the current timeout for scroll capture requests.
+     *
+     * @return the timeout in milliseconds
+     */
+    public long getScrollCaptureRequestTimeout() {
+        return mScrollCaptureRequestTimeout;
+    }
+
+    /**
+     * Handles an inbound request for scroll capture from the system. A search will be
+     * dispatched through the view tree to locate scrolling content.
      * <p>
-     * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect,
-     * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
-     * depending on the results of the search.
+     * A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
+     * will follow.
      *
      * @param callbacks to receive responses
-     * @see ScrollCaptureTargetResolver
+     * @see ScrollCaptureTargetSelector
      */
     public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results =
+                new ScrollCaptureSearchResults(mContext.getMainExecutor());
 
         // Window (root) level callbacks
-        collectRootScrollCaptureTargets(targetList);
+        collectRootScrollCaptureTargets(results);
 
         // Search through View-tree
         View rootView = getView();
@@ -9260,58 +9287,70 @@
             Point point = new Point();
             Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
             getChildVisibleRect(rootView, rect, point);
-            rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+            rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget);
         }
-
-        // No-op path. Scroll capture not offered for this window.
-        if (targetList.isEmpty()) {
-            dispatchScrollCaptureSearchResult(callbacks, null);
-            return;
+        Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results);
+        results.setOnCompleteListener(onComplete);
+        if (!results.isComplete()) {
+            mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout());
         }
-
-        // Request scrollBounds from each of the targets.
-        // Continues with the consumer once all responses are consumed, or the timeout expires.
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList);
-        resolver.start(mHandler, 1000,
-                (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected));
     }
 
     /** Called by {@link #handleScrollCaptureRequest} when a result is returned */
     private void dispatchScrollCaptureSearchResult(
             @NonNull IScrollCaptureCallbacks callbacks,
-            @Nullable ScrollCaptureTarget selectedTarget) {
+            @NonNull ScrollCaptureSearchResults results) {
 
-        // If timeout or no eligible targets found.
+        ScrollCaptureTarget selectedTarget = results.getTopResult();
+
+        ScrollCaptureResponse.Builder response = new ScrollCaptureResponse.Builder();
+        response.setWindowTitle(getTitle().toString());
+
+        StringWriter writer =  new StringWriter();
+        IndentingPrintWriter pw = new IndentingPrintWriter(writer);
+        results.dump(pw);
+        pw.flush();
+        response.addMessage(writer.toString());
+
         if (selectedTarget == null) {
+            response.setDescription("No scrollable targets found in window");
             try {
-                if (DEBUG_SCROLL_CAPTURE) {
-                    Log.d(TAG, "scrollCaptureSearch returned no targets available.");
-                }
-                callbacks.onUnavailable();
+                callbacks.onScrollCaptureResponse(response.build());
             } catch (RemoteException e) {
-                if (DEBUG_SCROLL_CAPTURE) {
-                    Log.w(TAG, "Failed to send scroll capture search result.", e);
-                }
+                Log.e(TAG, "Failed to send scroll capture search result", e);
             }
             return;
         }
 
-        // Create a client instance and return it to the caller
-        mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks);
+        response.setDescription("Connected");
+
+        // Compute area covered by scrolling content within window
+        Rect boundsInWindow = new Rect();
+        View containingView = selectedTarget.getContainingView();
+        containingView.getLocationInWindow(mAttachInfo.mTmpLocation);
+        boundsInWindow.set(selectedTarget.getScrollBounds());
+        boundsInWindow.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]);
+        response.setBoundsInWindow(boundsInWindow);
+
+        // Compute the area on screen covered by the window
+        Rect boundsOnScreen = new Rect();
+        mView.getLocationOnScreen(mAttachInfo.mTmpLocation);
+        boundsOnScreen.set(0, 0, mView.getWidth(), mView.getHeight());
+        boundsOnScreen.offset(mAttachInfo.mTmpLocation[0], mAttachInfo.mTmpLocation[1]);
+        response.setWindowBounds(boundsOnScreen);
+
+        // Create a connection and return it to the caller
+        ScrollCaptureConnection connection = new ScrollCaptureConnection(
+                mView.getContext().getMainExecutor(), selectedTarget, callbacks);
+        response.setConnection(connection);
+
         try {
-            if (DEBUG_SCROLL_CAPTURE) {
-                Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection());
-            }
-            callbacks.onConnected(
-                    mScrollCaptureConnection,
-                    selectedTarget.getScrollBounds(),
-                    selectedTarget.getPositionInWindow());
+            callbacks.onScrollCaptureResponse(response.build());
         } catch (RemoteException e) {
             if (DEBUG_SCROLL_CAPTURE) {
-                Log.w(TAG, "Failed to send scroll capture search result.", e);
+                Log.w(TAG, "Failed to send scroll capture search response.", e);
             }
-            mScrollCaptureConnection.disconnect();
-            mScrollCaptureConnection = null;
+            connection.close();
         }
     }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 4ecdd78..221b334 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2621,7 +2621,6 @@
      * callback with the root view of the window.
      *
      * @param callback the callback to add
-     * @hide
      */
     public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
@@ -2630,7 +2629,6 @@
      * Unregisters a {@link ScrollCaptureCallback} previously registered with this window.
      *
      * @param callback the callback to remove
-     * @hide
      */
     public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) {
     }
diff --git a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
index 96dac56..402d7fe 100644
--- a/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
+++ b/core/java/com/android/internal/graphics/drawable/BackgroundBlurDrawable.java
@@ -19,6 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UiThread;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -31,12 +32,14 @@
 import android.graphics.Rect;
 import android.graphics.RenderNode;
 import android.graphics.drawable.Drawable;
-import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
-import android.view.SurfaceControl;
+import android.util.LongSparseArray;
 import android.view.ViewRootImpl;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * A drawable that keeps track of a blur region, pokes a hole under it, and propagates its state
@@ -52,26 +55,40 @@
     private final Paint mPaint = new Paint();
     private final Path mRectPath = new Path();
     private final float[] mTmpRadii = new float[8];
-    private final SurfaceControl.BlurRegion mBlurRegion = new SurfaceControl.BlurRegion();
 
-    // This will be called from a thread pool.
-    private final RenderNode.PositionUpdateListener mPositionUpdateListener =
+    private boolean mVisible = true;
+
+    // Confined to UiThread. The values are copied into a BlurRegion, which lives on
+    // RenderThread to avoid interference with UiThread updates.
+    private int mBlurRadius;
+    private float mCornerRadiusTL;
+    private float mCornerRadiusTR;
+    private float mCornerRadiusBL;
+    private float mCornerRadiusBR;
+    private float mAlpha = 1;
+
+    // Do not update from UiThread. This holds the latest position for this drawable. It is used
+    // by the Aggregator from RenderThread to get the final position of the blur region sent to SF
+    private final Rect mRect = new Rect();
+    // This is called from a thread pool. The callbacks might come out of order w.r.t. the frame
+    // number, so we send a Runnable holding the actual update to the Aggregator. The Aggregator
+    // can apply the update on RenderThread when processing that same frame.
+    @VisibleForTesting
+    public final RenderNode.PositionUpdateListener mPositionUpdateListener =
             new RenderNode.PositionUpdateListener() {
             @Override
             public void positionChanged(long frameNumber, int left, int top, int right,
                     int bottom) {
-                synchronized (mAggregator) {
-                    mBlurRegion.rect.set(left, top, right, bottom);
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
-                }
+                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
+                    mRect.set(left, top, right, bottom);
+                });
             }
 
             @Override
             public void positionLost(long frameNumber) {
-                synchronized (mAggregator) {
-                    mBlurRegion.rect.setEmpty();
-                    mAggregator.onBlurRegionUpdated(BackgroundBlurDrawable.this, mBlurRegion);
-                }
+                mAggregator.onRenderNodePositionChanged(frameNumber, () -> {
+                    mRect.setEmpty();
+                });
             }
         };
 
@@ -79,6 +96,7 @@
         mAggregator = aggregator;
         mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         mPaint.setColor(Color.TRANSPARENT);
+        mPaint.setAntiAlias(true);
         mRenderNode = new RenderNode("BackgroundBlurDrawable");
         mRenderNode.addPositionUpdateListener(mPositionUpdateListener);
     }
@@ -104,23 +122,30 @@
     public boolean setVisible(boolean visible, boolean restart) {
         boolean changed = super.setVisible(visible, restart);
         if (changed) {
-            mBlurRegion.visible = visible;
+            mVisible = visible;
+            mAggregator.onBlurDrawableUpdated(this);
         }
         return changed;
     }
 
     @Override
     public void setAlpha(int alpha) {
-        mBlurRegion.alpha = alpha / 255f;
-        invalidateSelf();
+        if (mAlpha != alpha / 255f) {
+            mAlpha = alpha / 255f;
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
+        }
     }
 
     /**
      * Blur radius in pixels.
      */
     public void setBlurRadius(int blurRadius) {
-        mBlurRegion.blurRadius = blurRadius;
-        invalidateSelf();
+        if (mBlurRadius != blurRadius) {
+            mBlurRadius = blurRadius;
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
+        }
     }
 
     /**
@@ -139,14 +164,18 @@
      */
     public void setCornerRadius(float cornerRadiusTL, float cornerRadiusTR, float cornerRadiusBL,
             float cornerRadiusBR) {
-        synchronized (mAggregator) {
-            mBlurRegion.cornerRadiusTL = cornerRadiusTL;
-            mBlurRegion.cornerRadiusTR = cornerRadiusTR;
-            mBlurRegion.cornerRadiusBL = cornerRadiusBL;
-            mBlurRegion.cornerRadiusBR = cornerRadiusBR;
+        if (mCornerRadiusTL != cornerRadiusTL
+                || mCornerRadiusTR != cornerRadiusTR
+                || mCornerRadiusBL != cornerRadiusBL
+                || mCornerRadiusBR != cornerRadiusBR) {
+            mCornerRadiusTL = cornerRadiusTL;
+            mCornerRadiusTR = cornerRadiusTR;
+            mCornerRadiusBL = cornerRadiusBL;
+            mCornerRadiusBR = cornerRadiusBR;
+            updatePath();
+            invalidateSelf();
+            mAggregator.onBlurDrawableUpdated(this);
         }
-        updatePath();
-        invalidateSelf();
     }
 
     @Override
@@ -157,12 +186,10 @@
     }
 
     private void updatePath() {
-        synchronized (mAggregator) {
-            mTmpRadii[0] = mTmpRadii[1] = mBlurRegion.cornerRadiusTL;
-            mTmpRadii[2] = mTmpRadii[3] = mBlurRegion.cornerRadiusTR;
-            mTmpRadii[4] = mTmpRadii[5] = mBlurRegion.cornerRadiusBL;
-            mTmpRadii[6] = mTmpRadii[7] = mBlurRegion.cornerRadiusBR;
-        }
+        mTmpRadii[0] = mTmpRadii[1] = mCornerRadiusTL;
+        mTmpRadii[2] = mTmpRadii[3] = mCornerRadiusTR;
+        mTmpRadii[4] = mTmpRadii[5] = mCornerRadiusBL;
+        mTmpRadii[6] = mTmpRadii[7] = mCornerRadiusBR;
         mRectPath.reset();
         if (getAlpha() == 0 || !isVisible()) {
             return;
@@ -182,17 +209,32 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    @Override
+    public String toString() {
+        return "BackgroundBlurDrawable{"
+            + "blurRadius=" + mBlurRadius
+            + ", corners={" + mCornerRadiusTL
+            + "," + mCornerRadiusTR
+            + "," + mCornerRadiusBL
+            + "," + mCornerRadiusBR
+            + "}, alpha=" + mAlpha
+            + ", visible=" + mVisible
+            + "}";
+    }
+
     /**
      * Responsible for keeping track of all blur regions of a {@link ViewRootImpl} and posting a
      * message when it's time to propagate them.
      */
     public static final class Aggregator {
-
-        private final ArrayMap<BackgroundBlurDrawable, SurfaceControl.BlurRegion> mBlurRegions =
-                new ArrayMap<>();
+        private final Object mRtLock = new Object();
+        // Maintains a list of all *visible* blur drawables. Confined to  UI thread
+        private final ArraySet<BackgroundBlurDrawable> mDrawables = new ArraySet();
+        @GuardedBy("mRtLock")
+        private final LongSparseArray<ArraySet<Runnable>> mFrameRtUpdates = new LongSparseArray();
         private final ViewRootImpl mViewRoot;
-        private float[][] mTmpBlurRegionsArray;
-        private boolean mNeedsUpdate;
+        private BlurRegion[] mTmpBlurRegionsForFrame = new BlurRegion[0];
+        private boolean mHasUiUpdates;
 
         public Aggregator(ViewRootImpl viewRoot) {
             mViewRoot = viewRoot;
@@ -209,60 +251,191 @@
         }
 
         /**
-         * Called from RenderThread only, already locked.
-         * @param drawable
-         * @param blurRegion
+         * Called when a BackgroundBlurDrawable has been updated
          */
-        void onBlurRegionUpdated(BackgroundBlurDrawable drawable,
-                SurfaceControl.BlurRegion blurRegion) {
-            if (blurRegion.rect.isEmpty() || blurRegion.alpha == 0 || blurRegion.blurRadius == 0
-                    || !blurRegion.visible) {
-                mBlurRegions.remove(drawable);
-                mNeedsUpdate = true;
-                if (DEBUG) {
-                    Log.d(TAG, "Remove " + blurRegion);
+        @UiThread
+        void onBlurDrawableUpdated(BackgroundBlurDrawable drawable) {
+            final boolean shouldBeDrawn =
+                    drawable.mAlpha != 0 && drawable.mBlurRadius > 0 && drawable.mVisible;
+            final boolean isDrawn = mDrawables.contains(drawable);
+            if (shouldBeDrawn) {
+                mHasUiUpdates = true;
+                if (!isDrawn) {
+                    mDrawables.add(drawable);
+                    if (DEBUG) {
+                        Log.d(TAG, "Add " + drawable);
+                    }
+                } else {
+                    if (DEBUG) {
+                        Log.d(TAG, "Update " + drawable);
+                    }
                 }
-            } else {
-                mBlurRegions.put(drawable, blurRegion);
-                mNeedsUpdate = true;
+            } else if (!shouldBeDrawn && isDrawn) {
+                mHasUiUpdates = true;
+                mDrawables.remove(drawable);
                 if (DEBUG) {
-                    Log.d(TAG, "Update " + blurRegion);
+                    Log.d(TAG, "Remove " + drawable);
                 }
             }
         }
 
+        // Called from a thread pool
+        void onRenderNodePositionChanged(long frameNumber, Runnable update) {
+            // One of the blur region's position has changed, so we have to send an updated list
+            // of blur regions to SurfaceFlinger for this frame.
+            synchronized (mRtLock) {
+                ArraySet<Runnable> frameRtUpdates = mFrameRtUpdates.get(frameNumber);
+                if (frameRtUpdates == null) {
+                    frameRtUpdates = new ArraySet<>();
+                    mFrameRtUpdates.put(frameNumber, frameRtUpdates);
+                }
+                frameRtUpdates.add(update);
+            }
+        }
+
         /**
-         * If there are any blur regions visible on the screen at the moment.
+         * @return true if there are any updates that need to be sent to SF
          */
+        @UiThread
+        public boolean hasUpdates() {
+            return mHasUiUpdates;
+        }
+
+        /**
+         * @return true if there are any visible blur regions
+         */
+        @UiThread
         public boolean hasRegions() {
-            return mBlurRegions.size() > 0;
+            return mDrawables.size() > 0;
         }
 
         /**
-         * Dispatch blur updates, if there were any.
-         * @param frameNumber Frame where the update should happen.
+         * @return an array of BlurRegions, which are holding a copy of the information in
+         *         all the currently visible BackgroundBlurDrawables
          */
-        public void dispatchBlurTransactionIfNeeded(long frameNumber) {
-            synchronized (this) {
-                if (!mNeedsUpdate) {
-                    return;
+        @UiThread
+        public BlurRegion[] getBlurRegionsCopyForRT() {
+            if (mHasUiUpdates) {
+                mTmpBlurRegionsForFrame = new BlurRegion[mDrawables.size()];
+                for (int i = 0; i < mDrawables.size(); i++) {
+                    mTmpBlurRegionsForFrame[i] = new BlurRegion(mDrawables.valueAt(i));
                 }
-                mNeedsUpdate = false;
-
-                if (mTmpBlurRegionsArray == null
-                        || mTmpBlurRegionsArray.length != mBlurRegions.size()) {
-                    mTmpBlurRegionsArray = new float[mBlurRegions.size()][];
-                }
-                if (DEBUG) {
-                    Log.d(TAG, "onBlurRegionUpdated will dispatch " + mTmpBlurRegionsArray.length
-                            + " regions for frame " + frameNumber);
-                }
-                for (int i = 0; i < mTmpBlurRegionsArray.length; i++) {
-                    mTmpBlurRegionsArray[i] = mBlurRegions.valueAt(i).toFloatArray();
-                }
-
-                mViewRoot.dispatchBlurRegions(mTmpBlurRegionsArray, frameNumber);
+                mHasUiUpdates = false;
             }
+
+            return mTmpBlurRegionsForFrame;
+        }
+
+        /**
+         * Called on RenderThread.
+         *
+         * @return all blur regions if there are any ui or position updates for this frame,
+         *         null otherwise
+         */
+        @VisibleForTesting
+        public float[][] getBlurRegionsToDispatchToSf(long frameNumber,
+                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
+            synchronized (mRtLock) {
+                if (!hasUiUpdatesForFrame && (mFrameRtUpdates.size() == 0
+                            || mFrameRtUpdates.keyAt(0) > frameNumber)) {
+                    return null;
+                }
+
+                // mFrameRtUpdates holds position updates coming from a thread pool span from
+                // RenderThread. At this point, all position updates for frame frameNumber should
+                // have been added to mFrameRtUpdates.
+                // Here, we apply all updates for frames <= frameNumber in case some previous update
+                // has been missed. This also protects mFrameRtUpdates from memory leaks.
+                while (mFrameRtUpdates.size() != 0 && mFrameRtUpdates.keyAt(0) <= frameNumber) {
+                    final ArraySet<Runnable> frameUpdates = mFrameRtUpdates.valueAt(0);
+                    mFrameRtUpdates.removeAt(0);
+                    for (int i = 0; i < frameUpdates.size(); i++) {
+                        frameUpdates.valueAt(i).run();
+                    }
+                }
+            }
+
+            if (DEBUG) {
+                Log.d(TAG, "Dispatching " + blurRegionsForFrame.length + " blur regions:");
+            }
+
+            final float[][] blurRegionsArray = new float[blurRegionsForFrame.length][];
+            for (int i = 0; i < blurRegionsArray.length; i++) {
+                blurRegionsArray[i] = blurRegionsForFrame[i].toFloatArray();
+                if (DEBUG) {
+                    Log.d(TAG, blurRegionsForFrame[i].toString());
+                }
+            }
+            return blurRegionsArray;
+        }
+
+        /**
+         * Called on RenderThread in FrameDrawingCallback.
+         * Dispatch all blur regions if there are any ui or position updates.
+         */
+        public void dispatchBlurTransactionIfNeeded(long frameNumber,
+                BlurRegion[] blurRegionsForFrame, boolean hasUiUpdatesForFrame) {
+            final float[][] blurRegionsArray = getBlurRegionsToDispatchToSf(frameNumber,
+                    blurRegionsForFrame, hasUiUpdatesForFrame);
+            if (blurRegionsArray != null) {
+                mViewRoot.dispatchBlurRegions(blurRegionsArray, frameNumber);
+            }
+        }
+
+    }
+
+    /**
+     * Wrapper for sending blur data to SurfaceFlinger
+     * Confined to RenderThread.
+     */
+    public static final class BlurRegion {
+        public final int blurRadius;
+        public final float cornerRadiusTL;
+        public final float cornerRadiusTR;
+        public final float cornerRadiusBL;
+        public final float cornerRadiusBR;
+        public final float alpha;
+        public final Rect rect;
+
+        BlurRegion(BackgroundBlurDrawable drawable) {
+            alpha = drawable.mAlpha;
+            blurRadius = drawable.mBlurRadius;
+            cornerRadiusTL = drawable.mCornerRadiusTL;
+            cornerRadiusTR = drawable.mCornerRadiusTR;
+            cornerRadiusBL = drawable.mCornerRadiusBL;
+            cornerRadiusBR = drawable.mCornerRadiusBR;
+            rect = drawable.mRect;
+        }
+
+        /**
+         * Serializes this class into a float array that's more JNI friendly.
+         */
+        float[] toFloatArray() {
+            final float[] floatArray = new float[10];
+            floatArray[0] = blurRadius;
+            floatArray[1] = alpha;
+            floatArray[2] = rect.left;
+            floatArray[3] = rect.top;
+            floatArray[4] = rect.right;
+            floatArray[5] = rect.bottom;
+            floatArray[6] = cornerRadiusTL;
+            floatArray[7] = cornerRadiusTR;
+            floatArray[8] = cornerRadiusBL;
+            floatArray[9] = cornerRadiusBR;
+            return floatArray;
+        }
+
+        @Override
+        public String toString() {
+            return "BlurRegion{"
+                    + "blurRadius=" + blurRadius
+                    + ", corners={" + cornerRadiusTL
+                    + "," + cornerRadiusTR
+                    + "," + cornerRadiusBL
+                    + "," + cornerRadiusBR
+                    + "}, alpha=" + alpha
+                    + ", rect=" + rect
+                    + "}";
         }
     }
 }
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index 9698f19..791e9ad 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -1,6 +1,23 @@
 {
   "presubmit": [
     {
+      "file_patterns": ["Battery[^/]*\\.java"],
+      "name": "FrameworksCoreTests",
+      "options": [
+        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
+        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+      ]
+    },
+    {
+      "file_patterns": ["Battery[^/]*\\.java"],
+      "name": "FrameworksServicesTests",
+      "options": [
+        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
+        { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" },
+        { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" }
+      ]
+    },
+    {
       "name": "FrameworksCoreTests",
       "options": [
         {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index b99c953..6b1d408b 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -18,8 +18,8 @@
 
 import static android.system.OsConstants.O_CLOEXEC;
 
-import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.pm.ApplicationInfo;
 import android.net.Credentials;
 import android.net.LocalServerSocket;
@@ -36,17 +36,16 @@
 
 import com.android.internal.net.NetworkUtilsInternal;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 import dalvik.system.ZygoteHooks;
 
 import libcore.io.IoUtils;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.io.InputStreamReader;
 
 /** @hide */
 public final class Zygote {
@@ -238,6 +237,8 @@
      */
     public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
 
+    private static final String TAG = "Zygote";
+
     /** Prefix prepended to socket names created by init */
     private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
 
@@ -408,6 +409,10 @@
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
 
+        if (gids != null && gids.length > 0) {
+            NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
+        }
+
         // Set the Java Language thread priority to the default value for new apps.
         Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
 
@@ -580,114 +585,163 @@
     private static native int nativeGetUsapPoolEventFD();
 
     /**
-     * Fork a new unspecialized app process from the zygote
+     * Fork a new unspecialized app process from the zygote. Adds the Usap to the native
+     * Usap table.
      *
      * @param usapPoolSocket  The server socket the USAP will call accept on
-     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
-     * @param isPriorityFork  Value controlling the process priority level until accept is called
-     * @return In the Zygote process this function will always return null; in unspecialized app
-     *         processes this function will return a Runnable object representing the new
-     *         application that is passed up from usapMain.
+     * @param sessionSocketRawFDs  Anonymous session sockets that are currently open.
+     *         These are closed in the child.
+     * @param isPriorityFork Raise the initial process priority level because this is on the
+     *         critical path for application startup.
+     * @return In the child process, this returns a Runnable that waits for specialization
+     *         info to start an app process. In the sygote/parent process this returns null.
      */
-    static Runnable forkUsap(LocalServerSocket usapPoolSocket,
-                             int[] sessionSocketRawFDs,
-                             boolean isPriorityFork) {
-        FileDescriptor[] pipeFDs;
+    static @Nullable Runnable forkUsap(LocalServerSocket usapPoolSocket,
+                                       int[] sessionSocketRawFDs,
+                                       boolean isPriorityFork) {
+        FileDescriptor readFD;
+        FileDescriptor writeFD;
 
         try {
-            pipeFDs = Os.pipe2(O_CLOEXEC);
+            FileDescriptor[] pipeFDs = Os.pipe2(O_CLOEXEC);
+            readFD = pipeFDs[0];
+            writeFD = pipeFDs[1];
         } catch (ErrnoException errnoEx) {
             throw new IllegalStateException("Unable to create USAP pipe.", errnoEx);
         }
 
-        int pid =
-                nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
-                               sessionSocketRawFDs, isPriorityFork);
-
+        int pid = nativeForkApp(readFD.getInt$(), writeFD.getInt$(),
+                                sessionSocketRawFDs, /*argsKnown=*/ false, isPriorityFork);
         if (pid == 0) {
-            IoUtils.closeQuietly(pipeFDs[0]);
-            return usapMain(usapPoolSocket, pipeFDs[1]);
+            IoUtils.closeQuietly(readFD);
+            return childMain(null, usapPoolSocket, writeFD);
+        } else if (pid == -1) {
+            // Fork failed.
+            return null;
         } else {
-            // The read-end of the pipe will be closed by the native code.
-            // See removeUsapTableEntry();
-            IoUtils.closeQuietly(pipeFDs[1]);
+            // readFD will be closed by the native code. See removeUsapTableEntry();
+            IoUtils.closeQuietly(writeFD);
+            nativeAddUsapTableEntry(pid, readFD.getInt$());
             return null;
         }
     }
 
-    private static native int nativeForkUsap(int readPipeFD,
-                                             int writePipeFD,
-                                             int[] sessionSocketRawFDs,
-                                             boolean isPriorityFork);
+    private static native int nativeForkApp(int readPipeFD,
+                                            int writePipeFD,
+                                            int[] sessionSocketRawFDs,
+                                            boolean argsKnown,
+                                            boolean isPriorityFork);
 
     /**
-     * This function is used by unspecialized app processes to wait for specialization requests from
-     * the system server.
+     * Add an entry for a new Usap to the table maintained in native code.
+     */
+    @CriticalNative
+    private static native void nativeAddUsapTableEntry(int pid, int readPipeFD);
+
+    /**
+     * Fork a new app process from the zygote. argBuffer contains a fork command that
+     * request neither a child zygote, nor a wrapped process. Continue to accept connections
+     * on the specified socket, use those to refill argBuffer, and continue to process
+     * sufficiently simple fork requests. We presume that the only open file descriptors
+     * requiring special treatment are the session socket embedded in argBuffer, and
+     * zygoteSocket.
+     * @param argBuffer containing initial command and the connected socket from which to
+     *         read more
+     * @param zygoteSocket socket from which to obtain new connections when current argBuffer
+     *         one is disconnected
+     * @param expectedUId Uid of peer for initial requests. Subsequent requests from a different
+     *               peer will cause us to return rather than perform the requested fork.
+     * @param minUid Minimum Uid enforced for all but first fork request. The caller checks
+     *               the Uid policy for the initial request.
+     * @param firstNiceName name of first created process. Used for error reporting only.
+     * @return A Runnable in each child process, null in the parent.
+     * If this returns in then argBuffer still contains a command needing to be executed.
+     */
+    static @Nullable Runnable forkSimpleApps(@NonNull ZygoteCommandBuffer argBuffer,
+                                             @NonNull FileDescriptor zygoteSocket,
+                                             int expectedUid,
+                                             int minUid,
+                                             @Nullable String firstNiceName) {
+        boolean in_child =
+                argBuffer.forkRepeatedly(zygoteSocket, expectedUid, minUid, firstNiceName);
+        if (in_child) {
+            return childMain(argBuffer, /*usapPoolSocket=*/null, /*writePipe=*/null);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Specialize the current process into one described by argBuffer or the command read from
+     * usapPoolSocket. Exactly one of those must be null. If we are given an argBuffer, we close
+     * it. Used both for a specializing a USAP process, and for process creation without USAPs.
+     * In both cases, we specialize the process after first returning to Java code.
      *
      * @param writePipe  The write end of the reporting pipe used to communicate with the poll loop
      *                   of the ZygoteServer.
      * @return A runnable oject representing the new application.
      */
-    private static Runnable usapMain(LocalServerSocket usapPoolSocket,
-                                     FileDescriptor writePipe) {
+    private static Runnable childMain(@Nullable ZygoteCommandBuffer argBuffer,
+                                      @Nullable LocalServerSocket usapPoolSocket,
+                                      FileDescriptor writePipe) {
         final int pid = Process.myPid();
-        Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
 
-        LocalSocket sessionSocket = null;
         DataOutputStream usapOutputStream = null;
-        Credentials peerCredentials = null;
         ZygoteArguments args = null;
 
-        // Change the priority to max before calling accept so we can respond to new specialization
-        // requests as quickly as possible.  This will be reverted to the default priority in the
-        // native specialization code.
-        boostUsapPriority();
+        // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
+        blockSigTerm();
 
-        while (true) {
-            try {
-                sessionSocket = usapPoolSocket.accept();
+        LocalSocket sessionSocket = null;
+        if (argBuffer == null) {
+            // Read arguments from usapPoolSocket instead.
 
-                // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
-                blockSigTerm();
+            Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
 
-                BufferedReader usapReader =
-                        new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
-                usapOutputStream =
-                        new DataOutputStream(sessionSocket.getOutputStream());
+            // Change the priority to max before calling accept so we can respond to new
+            // specialization requests as quickly as possible.  This will be reverted to the
+            // default priority in the native specialization code.
+            boostUsapPriority();
 
-                peerCredentials = sessionSocket.getPeerCredentials();
+            while (true) {
+                ZygoteCommandBuffer tmpArgBuffer = null;
+                try {
+                    sessionSocket = usapPoolSocket.accept();
 
-                String[] argStrings = readArgumentList(usapReader);
-
-                if (argStrings != null) {
-                    args = new ZygoteArguments(argStrings);
-
+                    usapOutputStream =
+                            new DataOutputStream(sessionSocket.getOutputStream());
+                    Credentials peerCredentials = sessionSocket.getPeerCredentials();
+                    tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
+                    args = ZygoteArguments.getInstance(argBuffer);
+                    applyUidSecurityPolicy(args, peerCredentials);
                     // TODO (chriswailes): Should this only be run for debug builds?
                     validateUsapCommand(args);
                     break;
-                } else {
-                    Log.e("USAP", "Truncated command received.");
-                    IoUtils.closeQuietly(sessionSocket);
-
-                    // Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
-                    unblockSigTerm();
+                } catch (Exception ex) {
+                    Log.e("USAP", ex.getMessage());
                 }
-            } catch (Exception ex) {
-                Log.e("USAP", ex.getMessage());
-                IoUtils.closeQuietly(sessionSocket);
-
                 // Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
                 unblockSigTerm();
+                IoUtils.closeQuietly(sessionSocket);
+                IoUtils.closeQuietly(tmpArgBuffer);
+                blockSigTerm();
             }
+        } else {
+            try {
+                args = ZygoteArguments.getInstance(argBuffer);
+            } catch (Exception ex) {
+                Log.e("AppStartup", ex.getMessage());
+                throw new AssertionError("Failed to parse application start command", ex);
+            }
+            // peerCredentials were checked in parent.
         }
-
+        if (args == null) {
+            throw new AssertionError("Empty command line");
+        }
         try {
-            // SIGTERM is blocked on loop exit.  This prevents a USAP that is specializing from
-            // being killed during a pool flush.
+            // SIGTERM is blocked here.  This prevents a USAP that is specializing from being
+            // killed during a pool flush.
 
-            setAppProcessName(args, "USAP");
-
-            applyUidSecurityPolicy(args, peerCredentials);
             applyDebuggerSystemProperty(args);
 
             int[][] rlimits = null;
@@ -696,53 +750,57 @@
                 rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
             }
 
-            // This must happen before the SELinux policy for this process is
-            // changed when specializing.
-            try {
-                // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
-                // Process.ProcessStartResult object.
-                usapOutputStream.writeInt(pid);
-            } catch (IOException ioEx) {
-                Log.e("USAP", "Failed to write response to session socket: "
-                        + ioEx.getMessage());
-                throw new RuntimeException(ioEx);
-            } finally {
-                IoUtils.closeQuietly(sessionSocket);
-
+            if (argBuffer == null) {
+                // This must happen before the SELinux policy for this process is
+                // changed when specializing.
                 try {
-                    // This socket is closed using Os.close due to an issue with the implementation
-                    // of LocalSocketImp.close().  Because the raw FD is created by init and then
-                    // loaded from an environment variable (as opposed to being created by the
-                    // LocalSocketImpl itself) the current implementation will not actually close
-                    // the underlying FD.
-                    //
-                    // See b/130309968 for discussion of this issue.
-                    Os.close(usapPoolSocket.getFileDescriptor());
-                } catch (ErrnoException ex) {
-                    Log.e("USAP", "Failed to close USAP pool socket");
-                    throw new RuntimeException(ex);
+                    // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
+                    // Process.ProcessStartResult object.
+                    usapOutputStream.writeInt(pid);
+                } catch (IOException ioEx) {
+                    Log.e("USAP", "Failed to write response to session socket: "
+                            + ioEx.getMessage());
+                    throw new RuntimeException(ioEx);
+                } finally {
+                    try {
+                        // Since the raw FD is created by init and then loaded from an environment
+                        // variable (as opposed to being created by the LocalSocketImpl itself),
+                        // the LocalSocket/LocalSocketImpl does not own the Os-level socket. See
+                        // the spec for LocalSocket.createConnectedLocalSocket(FileDescriptor fd).
+                        // Thus closing the LocalSocket does not suffice. See b/130309968 for more
+                        // discussion.
+                        FileDescriptor fd = usapPoolSocket.getFileDescriptor();
+                        usapPoolSocket.close();
+                        Os.close(fd);
+                    } catch (ErrnoException | IOException ex) {
+                        Log.e("USAP", "Failed to close USAP pool socket");
+                        throw new RuntimeException(ex);
+                    }
                 }
             }
 
-            try {
-                ByteArrayOutputStream buffer =
-                        new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
-                DataOutputStream outputStream = new DataOutputStream(buffer);
+            if (writePipe != null) {
+                try {
+                    ByteArrayOutputStream buffer =
+                            new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
+                    DataOutputStream outputStream = new DataOutputStream(buffer);
 
-                // This is written as a long so that the USAP reporting pipe and USAP pool event FD
-                // handlers in ZygoteServer.runSelectLoop can be unified.  These two cases should
-                // both send/receive 8 bytes.
-                outputStream.writeLong(pid);
-                outputStream.flush();
-
-                Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
-            } catch (Exception ex) {
-                Log.e("USAP",
-                        String.format("Failed to write PID (%d) to pipe (%d): %s",
-                                pid, writePipe.getInt$(), ex.getMessage()));
-                throw new RuntimeException(ex);
-            } finally {
-                IoUtils.closeQuietly(writePipe);
+                    // This is written as a long so that the USAP reporting pipe and USAP pool
+                    // event FD handlers in ZygoteServer.runSelectLoop can be unified.  These two
+                    // cases should both send/receive 8 bytes.
+                    // TODO: Needs tweaking to handle the non-Usap invoke-with case, which expects
+                    // a different format.
+                    outputStream.writeLong(pid);
+                    outputStream.flush();
+                    Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
+                } catch (Exception ex) {
+                    Log.e("USAP",
+                            String.format("Failed to write PID (%d) to pipe (%d): %s",
+                                    pid, writePipe.getInt$(), ex.getMessage()));
+                    throw new RuntimeException(ex);
+                } finally {
+                    IoUtils.closeQuietly(writePipe);
+                }
             }
 
             specializeAppProcess(args.mUid, args.mGid, args.mGids,
@@ -849,13 +907,29 @@
         return nativeRemoveUsapTableEntry(usapPID);
     }
 
+    @CriticalNative
     private static native boolean nativeRemoveUsapTableEntry(int usapPID);
 
     /**
-     * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
+     * Return the minimum child uid that the given peer is allowed to create.
+     * uid 1000 (Process.SYSTEM_UID) may specify any uid &ge; 1000 in normal
      * operation. It may also specify any gid and setgroups() list it chooses.
      * In factory test mode, it may specify any UID.
-     *
+     */
+    static int minChildUid(Credentials peer) {
+        if (peer.getUid() == Process.SYSTEM_UID
+                && FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF) {
+            /* In normal operation, SYSTEM_UID can only specify a restricted
+             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
+             */
+            return Process.SYSTEM_UID;
+        } else {
+            return 0;
+        }
+    }
+
+    /*
+     * Adjust uid and gid arguments, ensuring that the security policy is satisfied.
      * @param args non-null; zygote spawner arguments
      * @param peer non-null; peer credentials
      * @throws ZygoteSecurityException Indicates a security issue when applying the UID based
@@ -864,17 +938,10 @@
     static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
             throws ZygoteSecurityException {
 
-        if (peer.getUid() == Process.SYSTEM_UID) {
-            /* In normal operation, SYSTEM_UID can only specify a restricted
-             * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
-             */
-            boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
-
-            if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
-                throw new ZygoteSecurityException(
-                        "System UID may not launch process with UID < "
-                        + Process.SYSTEM_UID);
-            }
+        if (args.mUidSpecified && (args.mUid < minChildUid(peer))) {
+            throw new ZygoteSecurityException(
+                    "System UID may not launch process with UID < "
+                    + Process.SYSTEM_UID);
         }
 
         // If not otherwise specified, uid and gid are inherited from peer
@@ -960,45 +1027,6 @@
     }
 
     /**
-     * Reads an argument list from the provided socket
-     * @return Argument list or null if EOF is reached
-     * @throws IOException passed straight through
-     */
-    static String[] readArgumentList(BufferedReader socketReader) throws IOException {
-        int argc;
-
-        try {
-            String argc_string = socketReader.readLine();
-
-            if (argc_string == null) {
-                // EOF reached.
-                return null;
-            }
-            argc = Integer.parseInt(argc_string);
-
-        } catch (NumberFormatException ex) {
-            Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
-            throw new IOException("Invalid wire format");
-        }
-
-        // See bug 1092107: large argc can be used for a DOS attack
-        if (argc > MAX_ZYGOTE_ARGC) {
-            throw new IOException("Max arg count exceeded");
-        }
-
-        String[] args = new String[argc];
-        for (int arg_index = 0; arg_index < argc; arg_index++) {
-            args[arg_index] = socketReader.readLine();
-            if (args[arg_index] == null) {
-                // We got an unexpected EOF.
-                throw new IOException("Truncated request");
-            }
-        }
-
-        return args;
-    }
-
-    /**
      * Creates a managed LocalServerSocket object using a file descriptor
      * created by an init.rc script.  The init scripts that specify the
      * sockets name can be found in system/core/rootdir.  The socket is bound
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 32b808a..65b454d 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.os;
 
+import java.io.EOFException;
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * Handles argument parsing for args related to the zygote spawner.
@@ -245,20 +245,34 @@
     /**
      * Constructs instance and parses args
      *
-     * @param args zygote command-line args
+     * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count.
      */
-    ZygoteArguments(String[] args) throws IllegalArgumentException {
-        parseArgs(args);
+    private ZygoteArguments(ZygoteCommandBuffer args, int argCount)
+            throws IllegalArgumentException, EOFException {
+        parseArgs(args, argCount);
+    }
+
+    /**
+     * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return
+     * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially
+     * positioned at the beginning of the command.
+     */
+    public static ZygoteArguments getInstance(ZygoteCommandBuffer args)
+            throws IllegalArgumentException, EOFException {
+        int argCount = args.getCount();
+        return argCount == 0 ? null : new ZygoteArguments(args, argCount);
     }
 
     /**
      * Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
-     * "--setgid=") and creates an array containing the remaining args.
+     * "--setgid=") and creates an array containing the remaining args. Return false if we were
+     * at EOF.
      *
      * Per security review bug #1112214, duplicate args are disallowed in critical cases to make
      * injection harder.
      */
-    private void parseArgs(String[] args) throws IllegalArgumentException {
+    private void parseArgs(ZygoteCommandBuffer args, int argCount)
+            throws IllegalArgumentException, EOFException {
         /*
          * See android.os.ZygoteProcess.zygoteSendArgsAndGetResult()
          * Presently the wire format to the zygote process is:
@@ -269,13 +283,13 @@
          * the child or -1 on failure.
          */
 
-        int curArg = 0;
-
+        String unprocessedArg = null;
+        int curArg = 0;  // Index of arg
         boolean seenRuntimeArgs = false;
-
         boolean expectRuntimeArgs = true;
-        for ( /* curArg */ ; curArg < args.length; curArg++) {
-            String arg = args[curArg];
+
+        for ( /* curArg */ ; curArg < argCount; ++curArg) {
+            String arg = args.nextArg();
 
             if (arg.equals("--")) {
                 curArg++;
@@ -367,7 +381,8 @@
                         "Duplicate arg specified");
                 }
                 try {
-                    mInvokeWith = args[++curArg];
+                    ++curArg;
+                    mInvokeWith = args.nextArg();
                 } catch (IndexOutOfBoundsException ex) {
                     throw new IllegalArgumentException(
                         "--invoke-with requires argument");
@@ -397,12 +412,14 @@
             } else if (arg.startsWith("--app-data-dir=")) {
                 mAppDataDir = getAssignmentValue(arg);
             } else if (arg.equals("--preload-app")) {
-                mPreloadApp = args[++curArg];
+                ++curArg;
+                mPreloadApp = args.nextArg();
             } else if (arg.equals("--preload-package")) {
-                mPreloadPackage = args[++curArg];
-                mPreloadPackageLibs = args[++curArg];
-                mPreloadPackageLibFileName = args[++curArg];
-                mPreloadPackageCacheKey = args[++curArg];
+                curArg += 4;
+                mPreloadPackage = args.nextArg();
+                mPreloadPackageLibs = args.nextArg();
+                mPreloadPackageLibFileName = args.nextArg();
+                mPreloadPackageCacheKey = args.nextArg();
             } else if (arg.equals("--preload-default")) {
                 mPreloadDefault = true;
                 expectRuntimeArgs = false;
@@ -411,8 +428,11 @@
             } else if (arg.equals("--set-api-denylist-exemptions")) {
                 // consume all remaining args; this is a stand-alone command, never included
                 // with the regular fork command.
-                mApiDenylistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
-                curArg = args.length;
+                mApiDenylistExemptions = new String[argCount - curArg - 1];
+                ++curArg;
+                for (int i = 0; curArg < argCount; ++curArg, ++i) {
+                    mApiDenylistExemptions[i] = args.nextArg();
+                }
                 expectRuntimeArgs = false;
             } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
                 String rateStr = getAssignmentValue(arg);
@@ -462,35 +482,46 @@
             } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
                 mBindMountAppDataDirs = true;
             } else {
+                unprocessedArg = arg;
                 break;
             }
         }
+        // curArg is the index of the first unprocessed argument. That argument is either referenced
+        // by unprocessedArg or not read yet.
 
         if (mBootCompleted) {
-            if (args.length - curArg > 0) {
+            if (argCount > curArg) {
                 throw new IllegalArgumentException("Unexpected arguments after --boot-completed");
             }
         } else if (mAbiListQuery || mPidQuery) {
-            if (args.length - curArg > 0) {
+            if (argCount > curArg) {
                 throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
             }
         } else if (mPreloadPackage != null) {
-            if (args.length - curArg > 0) {
+            if (argCount > curArg) {
                 throw new IllegalArgumentException(
                     "Unexpected arguments after --preload-package.");
             }
         } else if (mPreloadApp != null) {
-            if (args.length - curArg > 0) {
+            if (argCount > curArg) {
                 throw new IllegalArgumentException(
                     "Unexpected arguments after --preload-app.");
             }
         } else if (expectRuntimeArgs) {
             if (!seenRuntimeArgs) {
-                throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
+                throw new IllegalArgumentException("Unexpected argument : "
+                    + (unprocessedArg == null ? args.nextArg() : unprocessedArg));
             }
 
-            mRemainingArgs = new String[args.length - curArg];
-            System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
+            mRemainingArgs = new String[argCount - curArg];
+            int i = 0;
+            if (unprocessedArg != null) {
+                mRemainingArgs[0] = unprocessedArg;
+                ++i;
+            }
+            for (; i < argCount - curArg; ++i) {
+                mRemainingArgs[i] = args.nextArg();
+            }
         }
 
         if (mStartChildZygote) {
diff --git a/core/java/com/android/internal/os/ZygoteCommandBuffer.java b/core/java/com/android/internal/os/ZygoteCommandBuffer.java
new file mode 100644
index 0000000..b61ae7a
--- /dev/null
+++ b/core/java/com/android/internal/os/ZygoteCommandBuffer.java
@@ -0,0 +1,187 @@
+/*
+ * 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.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LocalSocket;
+
+import java.io.FileDescriptor;
+import java.lang.ref.Reference;  // For reachabilityFence.
+
+/**
+ * A native-accessible buffer for Zygote commands. Designed to support repeated forking
+ * of applications without intervening memory allocation, thus keeping zygote memory
+ * as stable as possible.
+ * A ZygoteCommandBuffer may have an associated socket from which it can be refilled.
+ * Otherwise the contents are explicitly set by getInstance().
+ *
+ * NOT THREAD-SAFE. No methods may be called concurrently from multiple threads.
+ *
+ * Only one ZygoteCommandBuffer can exist at a time.
+ * Must be explicitly closed before being dropped.
+ * @hide
+ */
+class ZygoteCommandBuffer implements AutoCloseable {
+    private long mNativeBuffer;  // Not final so that we can clear it in close().
+
+    /**
+     * The command socket.
+     *
+     * mSocket is retained in the child process in "peer wait" mode, so
+     * that it closes when the child process terminates. In other cases,
+     * it is closed in the peer.
+     */
+    private final LocalSocket mSocket;
+    private final int mNativeSocket;
+
+    /**
+     * Constructs instance from file descriptor from which the command will be read.
+     * Only a single instance may be live in a given process. The native code checks.
+     *
+     * @param fd file descriptor to read from. The setCommand() method may be used if and only if
+     * fd is null.
+     */
+    ZygoteCommandBuffer(@Nullable LocalSocket socket) {
+        mSocket = socket;
+        if (socket == null) {
+            mNativeSocket = -1;
+        } else {
+            mNativeSocket = mSocket.getFileDescriptor().getInt$();
+        }
+        mNativeBuffer = getNativeBuffer(mNativeSocket);
+    }
+
+    /**
+     * Constructs an instance with explicitly supplied arguments and an invalid
+     * file descriptor. Can only be used for a single command.
+     */
+    ZygoteCommandBuffer(@NonNull String[] args) {
+        this((LocalSocket) null);
+        setCommand(args);
+    }
+
+
+    private static native long getNativeBuffer(int fd);
+
+    /**
+     * Deallocate native resources associated with the one and only command buffer, and prevent
+     * reuse. Subsequent calls to getInstance() will yield a new buffer.
+     * We do not close the associated socket, if any.
+     */
+    @Override
+    public void close() {
+        freeNativeBuffer(mNativeBuffer);
+        mNativeBuffer = 0;
+    }
+
+    private static native void freeNativeBuffer(long /* NativeCommandBuffer* */ nbuffer);
+
+    /**
+     * Read at least the first line of the next command into the buffer, return the argument count
+     * from that line. Assumes we are initially positioned at the beginning of the first line of
+     * the command. Leave the buffer positioned at the beginning of the second command line, i.e.
+     * the first argument. If the buffer has no associated file descriptor, we just reposition to
+     * the beginning of the buffer, and reread existing contents.  Returns zero if we started out
+     * at EOF.
+     */
+    int getCount() {
+        try {
+            return nativeGetCount(mNativeBuffer);
+        } finally {
+            // Make sure the mNativeSocket doesn't get closed due to early finalization.
+            Reference.reachabilityFence(mSocket);
+        }
+    }
+
+    private static native int nativeGetCount(long /* NativeCommandBuffer* */ nbuffer);
+
+
+    /*
+     * Set the buffer to contain the supplied sequence of arguments.
+     */
+    private void setCommand(String[] command) {
+        int nArgs = command.length;
+        insert(mNativeBuffer, Integer.toString(nArgs));
+        for (String s: command) {
+            insert(mNativeBuffer, s);
+        }
+        // Native code checks there is no socket; hence no reachabilityFence.
+    }
+
+    private static native void insert(long /* NativeCommandBuffer* */ nbuffer, String s);
+
+    /**
+     * Retrieve the next argument/line from the buffer, filling the buffer as necessary.
+     */
+    String nextArg() {
+        try {
+            return nativeNextArg(mNativeBuffer);
+        } finally {
+            Reference.reachabilityFence(mSocket);
+        }
+    }
+
+    private static native String nativeNextArg(long /* NativeCommandBuffer* */ nbuffer);
+
+    void readFullyAndReset() {
+        try {
+            nativeReadFullyAndReset(mNativeBuffer);
+        } finally {
+            Reference.reachabilityFence(mSocket);
+        }
+    }
+
+    private static native void nativeReadFullyAndReset(long /* NativeCommandBuffer* */ nbuffer);
+
+    /**
+     * Fork a child as specified by the current command in the buffer, and repeat this process
+     * after refilling the buffer, so long as the buffer clearly contains another fork command.
+     *
+     * @param zygoteSocket socket from which to obtain new connections when current one is
+     *         disconnected
+     * @param expectedUid Peer UID for current connection. We refuse to deal with requests from
+     *         a different UID.
+     * @param minUid the smallest uid that may be request for the child process.
+     * @param firstNiceName The name for the initial process to be forked. Used only for error
+     *         reporting.
+     *
+     * @return true in the child, false in the parent. In the parent case, the buffer is positioned
+     * at the beginning of a command that still needs to be processed.
+     */
+    boolean forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid,
+                       String firstNiceName) {
+        try {
+            return nativeForkRepeatedly(mNativeBuffer, zygoteSocket.getInt$(),
+                    expectedUid, minUid, firstNiceName);
+        } finally {
+            Reference.reachabilityFence(mSocket);
+            Reference.reachabilityFence(zygoteSocket);
+        }
+    }
+
+    /*
+     * Repeatedly fork children as above. It commonly does not return in the parent, but it may.
+     * @return true in the chaild, false in the parent if we encounter a command we couldn't handle.
+     */
+    private static native boolean nativeForkRepeatedly(long /* NativeCommandBuffer* */ nbuffer,
+                                                   int zygoteSocketRawFd,
+                                                   int expectedUid,
+                                                   int minUid,
+                                                   String firstNiceName);
+
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 5a576ebb..37c7590 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -36,16 +36,15 @@
 import android.util.Log;
 
 import dalvik.system.VMRuntime;
+import dalvik.system.ZygoteHooks;
 
 import libcore.io.IoUtils;
 
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 import java.util.concurrent.TimeUnit;
@@ -67,7 +66,6 @@
     private final LocalSocket mSocket;
     @UnsupportedAppUsage
     private final DataOutputStream mSocketOutStream;
-    private final BufferedReader mSocketReader;
     @UnsupportedAppUsage
     private final Credentials peer;
     private final String abiList;
@@ -85,9 +83,6 @@
         this.abiList = abiList;
 
         mSocketOutStream = new DataOutputStream(socket.getOutputStream());
-        mSocketReader =
-                new BufferedReader(
-                        new InputStreamReader(socket.getInputStream()), Zygote.SOCKET_BUFFER_SIZE);
 
         mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
 
@@ -111,178 +106,216 @@
     }
 
     /**
-     * Reads one start command from the command socket. If successful, a child is forked and a
+     * Reads a command from the command socket. If a child is successfully forked, a
      * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
      * process. {@code null} is always returned in the parent process (the zygote).
+     * If multipleOK is set, we may keep processing additional fork commands before returning.
      *
      * If the client closes the socket, an {@code EOF} condition is set, which callers can test
      * for by calling {@code ZygoteConnection.isClosedByPeer}.
      */
-    Runnable processOneCommand(ZygoteServer zygoteServer) {
-        String[] args;
+    Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
+        ZygoteArguments parsedArgs;
 
-        try {
-            args = Zygote.readArgumentList(mSocketReader);
-        } catch (IOException ex) {
-            throw new IllegalStateException("IOException on command socket", ex);
+        try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
+            while (true) {
+                try {
+                    parsedArgs = ZygoteArguments.getInstance(argBuffer);
+                    // Keep argBuffer around, since we need it to fork.
+                } catch (IOException ex) {
+                    throw new IllegalStateException("IOException on command socket", ex);
+                }
+                if (parsedArgs == null) {
+                    isEof = true;
+                    return null;
+                }
+
+                int pid;
+                FileDescriptor childPipeFd = null;
+                FileDescriptor serverPipeFd = null;
+
+                if (parsedArgs.mBootCompleted) {
+                    handleBootCompleted();
+                    return null;
+                }
+
+                if (parsedArgs.mAbiListQuery) {
+                    handleAbiListQuery();
+                    return null;
+                }
+
+                if (parsedArgs.mPidQuery) {
+                    handlePidQuery();
+                    return null;
+                }
+
+                if (parsedArgs.mUsapPoolStatusSpecified) {
+                    // Handle this once we've released the argBuffer, to avoid opening a second one.
+                    break;
+                }
+
+                if (parsedArgs.mPreloadDefault) {
+                    handlePreload();
+                    return null;
+                }
+
+                if (parsedArgs.mPreloadPackage != null) {
+                    handlePreloadPackage(parsedArgs.mPreloadPackage,
+                            parsedArgs.mPreloadPackageLibs,
+                            parsedArgs.mPreloadPackageLibFileName,
+                            parsedArgs.mPreloadPackageCacheKey);
+                    return null;
+                }
+
+                if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
+                    byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
+                    Parcel appInfoParcel = Parcel.obtain();
+                    appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
+                    appInfoParcel.setDataPosition(0);
+                    ApplicationInfo appInfo =
+                            ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
+                    appInfoParcel.recycle();
+                    if (appInfo != null) {
+                        handlePreloadApp(appInfo);
+                    } else {
+                        throw new IllegalArgumentException("Failed to deserialize --preload-app");
+                    }
+                    return null;
+                }
+
+                if (parsedArgs.mApiDenylistExemptions != null) {
+                    return handleApiDenylistExemptions(zygoteServer,
+                            parsedArgs.mApiDenylistExemptions);
+                }
+
+                if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
+                        || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
+                    return handleHiddenApiAccessLogSampleRate(zygoteServer,
+                            parsedArgs.mHiddenApiAccessLogSampleRate,
+                            parsedArgs.mHiddenApiAccessStatslogSampleRate);
+                }
+
+                if (parsedArgs.mPermittedCapabilities != 0
+                        || parsedArgs.mEffectiveCapabilities != 0) {
+                    throw new ZygoteSecurityException("Client may not specify capabilities: "
+                            + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
+                            + ", effective=0x"
+                            + Long.toHexString(parsedArgs.mEffectiveCapabilities));
+                }
+
+                Zygote.applyUidSecurityPolicy(parsedArgs, peer);
+                Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
+
+                Zygote.applyDebuggerSystemProperty(parsedArgs);
+                Zygote.applyInvokeWithSystemProperty(parsedArgs);
+
+                int[][] rlimits = null;
+
+                if (parsedArgs.mRLimits != null) {
+                    rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
+                }
+
+                int[] fdsToIgnore = null;
+
+                if (parsedArgs.mInvokeWith != null) {
+                    try {
+                        FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
+                        childPipeFd = pipeFds[1];
+                        serverPipeFd = pipeFds[0];
+                        Os.fcntlInt(childPipeFd, F_SETFD, 0);
+                        fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
+                    } catch (ErrnoException errnoEx) {
+                        throw new IllegalStateException("Unable to set up pipe for invoke-with",
+                                errnoEx);
+                    }
+                }
+
+                /*
+                 * In order to avoid leaking descriptors to the Zygote child,
+                 * the native code must close the two Zygote socket descriptors
+                 * in the child process before it switches from Zygote-root to
+                 * the UID and privileges of the application being launched.
+                 *
+                 * In order to avoid "bad file descriptor" errors when the
+                 * two LocalSocket objects are closed, the Posix file
+                 * descriptors are released via a dup2() call which closes
+                 * the socket and substitutes an open descriptor to /dev/null.
+                 */
+
+                int [] fdsToClose = { -1, -1 };
+
+                FileDescriptor fd = mSocket.getFileDescriptor();
+
+                if (fd != null) {
+                    fdsToClose[0] = fd.getInt$();
+                }
+
+                FileDescriptor zygoteFd = zygoteServer.getZygoteSocketFileDescriptor();
+
+                if (zygoteFd != null) {
+                    fdsToClose[1] = zygoteFd.getInt$();
+                }
+
+                if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
+                        || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
+                    // Continue using old code for now. TODO: Handle these cases in the other path.
+                    pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
+                            parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
+                            parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
+                            fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
+                            parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
+                            parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
+                            parsedArgs.mWhitelistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
+                            parsedArgs.mBindMountAppStorageDirs);
+
+                    try {
+                        if (pid == 0) {
+                            // in child
+                            zygoteServer.setForkChild();
+
+                            zygoteServer.closeServerSocket();
+                            IoUtils.closeQuietly(serverPipeFd);
+                            serverPipeFd = null;
+
+                            return handleChildProc(parsedArgs, childPipeFd,
+                                    parsedArgs.mStartChildZygote);
+                        } else {
+                            // In the parent. A pid < 0 indicates a failure and will be handled in
+                            // handleParentProc.
+                            IoUtils.closeQuietly(childPipeFd);
+                            childPipeFd = null;
+                            handleParentProc(pid, serverPipeFd);
+                            return null;
+                        }
+                    } finally {
+                        IoUtils.closeQuietly(childPipeFd);
+                        IoUtils.closeQuietly(serverPipeFd);
+                    }
+                } else {
+                    ZygoteHooks.preFork();
+                    Runnable result = Zygote.forkSimpleApps(argBuffer,
+                            zygoteServer.getZygoteSocketFileDescriptor(),
+                            peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);
+                    if (result == null) {
+                        // parent; we finished some number of forks. Result is Boolean.
+                        // We already did the equivalent of handleParentProc().
+                        ZygoteHooks.postForkCommon();
+                        // argBuffer contains a command not understood by forksimpleApps.
+                        continue;
+                    } else {
+                        // child; result is a Runnable.
+                        zygoteServer.setForkChild();
+                        Zygote.setAppProcessName(parsedArgs, TAG);  // ??? Necessary?
+                        return result;
+                    }
+                }
+            }
         }
-
-        // readArgumentList returns null only when it has reached EOF with no available
-        // data to read. This will only happen when the remote socket has disconnected.
-        if (args == null) {
-            isEof = true;
-            return null;
-        }
-
-        int pid;
-        FileDescriptor childPipeFd = null;
-        FileDescriptor serverPipeFd = null;
-
-        ZygoteArguments parsedArgs = new ZygoteArguments(args);
-
-        if (parsedArgs.mBootCompleted) {
-            handleBootCompleted();
-            return null;
-        }
-
-        if (parsedArgs.mAbiListQuery) {
-            handleAbiListQuery();
-            return null;
-        }
-
-        if (parsedArgs.mPidQuery) {
-            handlePidQuery();
-            return null;
-        }
-
         if (parsedArgs.mUsapPoolStatusSpecified) {
+            // Now that we've released argBuffer:
             return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
         }
-
-        if (parsedArgs.mPreloadDefault) {
-            handlePreload();
-            return null;
-        }
-
-        if (parsedArgs.mPreloadPackage != null) {
-            handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
-                    parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
-            return null;
-        }
-
-        if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
-            byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
-            Parcel appInfoParcel = Parcel.obtain();
-            appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
-            appInfoParcel.setDataPosition(0);
-            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
-            appInfoParcel.recycle();
-            if (appInfo != null) {
-                handlePreloadApp(appInfo);
-            } else {
-                throw new IllegalArgumentException("Failed to deserialize --preload-app");
-            }
-            return null;
-        }
-
-        if (parsedArgs.mApiDenylistExemptions != null) {
-            return handleApiDenylistExemptions(zygoteServer, parsedArgs.mApiDenylistExemptions);
-        }
-
-        if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
-                || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
-            return handleHiddenApiAccessLogSampleRate(zygoteServer,
-                    parsedArgs.mHiddenApiAccessLogSampleRate,
-                    parsedArgs.mHiddenApiAccessStatslogSampleRate);
-        }
-
-        if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
-            throw new ZygoteSecurityException("Client may not specify capabilities: "
-                    + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
-                    + ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
-        }
-
-        Zygote.applyUidSecurityPolicy(parsedArgs, peer);
-        Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
-
-        Zygote.applyDebuggerSystemProperty(parsedArgs);
-        Zygote.applyInvokeWithSystemProperty(parsedArgs);
-
-        int[][] rlimits = null;
-
-        if (parsedArgs.mRLimits != null) {
-            rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
-        }
-
-        int[] fdsToIgnore = null;
-
-        if (parsedArgs.mInvokeWith != null) {
-            try {
-                FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
-                childPipeFd = pipeFds[1];
-                serverPipeFd = pipeFds[0];
-                Os.fcntlInt(childPipeFd, F_SETFD, 0);
-                fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
-            } catch (ErrnoException errnoEx) {
-                throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
-            }
-        }
-
-        /*
-         * In order to avoid leaking descriptors to the Zygote child,
-         * the native code must close the two Zygote socket descriptors
-         * in the child process before it switches from Zygote-root to
-         * the UID and privileges of the application being launched.
-         *
-         * In order to avoid "bad file descriptor" errors when the
-         * two LocalSocket objects are closed, the Posix file
-         * descriptors are released via a dup2() call which closes
-         * the socket and substitutes an open descriptor to /dev/null.
-         */
-
-        int [] fdsToClose = { -1, -1 };
-
-        FileDescriptor fd = mSocket.getFileDescriptor();
-
-        if (fd != null) {
-            fdsToClose[0] = fd.getInt$();
-        }
-
-        fd = zygoteServer.getZygoteSocketFileDescriptor();
-
-        if (fd != null) {
-            fdsToClose[1] = fd.getInt$();
-        }
-
-        pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
-                parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
-                parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
-                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
-                parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
-                parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
-
-        try {
-            if (pid == 0) {
-                // in child
-                zygoteServer.setForkChild();
-
-                zygoteServer.closeServerSocket();
-                IoUtils.closeQuietly(serverPipeFd);
-                serverPipeFd = null;
-
-                return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
-            } else {
-                // In the parent. A pid < 0 indicates a failure and will be handled in
-                // handleParentProc.
-                IoUtils.closeQuietly(childPipeFd);
-                childPipeFd = null;
-                handleParentProc(pid, serverPipeFd);
-                return null;
-            }
-        } finally {
-            IoUtils.closeQuietly(childPipeFd);
-            IoUtils.closeQuietly(serverPipeFd);
-        }
+        throw new AssertionError("Shouldn't get here");
     }
 
     private void handleAbiListQuery() {
@@ -557,7 +590,7 @@
 
                     if (res > 0) {
                         if ((fds[0].revents & POLLIN) != 0) {
-                            // Only read one byte, so as not to block.
+                            // Only read one byte, so as not to block. Really needed?
                             int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
                             if (readBytes < 0) {
                                 throw new RuntimeException("Some error");
diff --git a/core/java/com/android/internal/os/ZygoteConnectionConstants.java b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
index 506e39f..0c1cd6d 100644
--- a/core/java/com/android/internal/os/ZygoteConnectionConstants.java
+++ b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
@@ -31,9 +31,6 @@
      */
     public static final int CONNECTION_TIMEOUT_MILLIS = 1000;
 
-    /** max number of arguments that a connection can specify */
-    public static final int MAX_ZYGOTE_ARGC = 1024;
-
     /**
      * Wait time for a wrapped app to report back its pid.
      *
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index f2ed50e..d5b778e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -65,6 +65,7 @@
 import libcore.io.IoUtils;
 
 import java.io.BufferedReader;
+import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -782,7 +783,13 @@
         int pid;
 
         try {
-            parsedArgs = new ZygoteArguments(args);
+            ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
+            try {
+                parsedArgs = ZygoteArguments.getInstance(commandBuffer);
+            } catch (EOFException e) {
+                throw new AssertionError("Unexpected argument error for forking system server", e);
+            }
+            commandBuffer.close();
             Zygote.applyDebuggerSystemProperty(parsedArgs);
             Zygote.applyInvokeWithSystemProperty(parsedArgs);
 
@@ -861,7 +868,7 @@
      * into new processes are required to either set the priority to the default value or terminate
      * before executing any non-system code.  The native side of this occurs in SpecializeCommon,
      * while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess,
-     * ZygoteConnection.handleChildProc, and Zygote.usapMain.
+     * ZygoteConnection.handleChildProc, and Zygote.childMain.
      *
      * @param argv  Command line arguments used to specify the Zygote's configuration.
      */
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 585ddf6..f71b314 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -337,7 +337,7 @@
      * @param sessionSocketRawFDs  Anonymous session sockets that are currently open
      * @return In the Zygote process this function will always return null; in unspecialized app
      *         processes this function will return a Runnable object representing the new
-     *         application that is passed up from usapMain.
+     *         application that is passed up from childMain (the usap's main wait loop).
      */
 
     Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
@@ -420,6 +420,7 @@
      * Runs the zygote process's select loop. Accepts new connections as
      * they happen, and reads commands from connections one spawn-request's
      * worth at a time.
+     * @param abiList list of ABIs supported by this zygote.
      */
     Runnable runSelectLoop(String abiList) {
         ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
@@ -537,22 +538,23 @@
 
                     if (pollIndex == 0) {
                         // Zygote server socket
-
                         ZygoteConnection newPeer = acceptCommandPeer(abiList);
                         peers.add(newPeer);
                         socketFDs.add(newPeer.getFileDescriptor());
-
                     } else if (pollIndex < usapPoolEventFDIndex) {
                         // Session socket accepted from the Zygote server socket
 
                         try {
                             ZygoteConnection connection = peers.get(pollIndex);
-                            final Runnable command = connection.processOneCommand(this);
+                            boolean multipleForksOK = !isUsapPoolEnabled()
+                                    && ZygoteHooks.indefiniteThreadSuspensionOK();
+                            final Runnable command =
+                                    connection.processCommand(this, multipleForksOK);
 
                             // TODO (chriswailes): Is this extra check necessary?
                             if (mIsForkChild) {
                                 // We're in the child. We should always have a command to run at
-                                // this stage if processOneCommand hasn't called "exec".
+                                // this stage if processCommand hasn't called "exec".
                                 if (command == null) {
                                     throw new IllegalStateException("command == null");
                                 }
@@ -565,7 +567,7 @@
                                 }
 
                                 // We don't know whether the remote side of the socket was closed or
-                                // not until we attempt to read from it from processOneCommand. This
+                                // not until we attempt to read from it from processCommand. This
                                 // shows up as a regular POLLIN event in our regular processing
                                 // loop.
                                 if (connection.isClosedByPeer()) {
diff --git a/core/java/com/android/internal/power/TEST_MAPPING b/core/java/com/android/internal/power/TEST_MAPPING
new file mode 100644
index 0000000..96f31bc
--- /dev/null
+++ b/core/java/com/android/internal/power/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
+        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+      ]
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
+        { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" },
+        { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" }
+      ]
+    }
+  ]
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ef2275d..ab0149f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -30,6 +30,7 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.PointerIcon;
+import android.view.ScrollCaptureResponse;
 import android.view.WindowInsets.Type.InsetsType;
 import android.window.ClientWindowFrames;
 
@@ -160,7 +161,9 @@
     @Override
     public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
         try {
-            callbacks.onUnavailable();
+            callbacks.onScrollCaptureResponse(
+                    new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
+
         } catch (RemoteException ex) {
             // ignore
         }
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 85fa791..a41511b 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -16,15 +16,19 @@
 
 package com.android.internal.view;
 
+import android.annotation.UiThread;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.HardwareRenderer;
 import android.graphics.Matrix;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.RenderNode;
-import android.os.Handler;
+import android.os.CancellationSignal;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display.ColorMode;
 import android.view.ScrollCaptureCallback;
 import android.view.ScrollCaptureSession;
 import android.view.Surface;
@@ -44,32 +48,42 @@
  * @param <V> the specific View subclass handled
  * @see ScrollCaptureViewHelper
  */
+@UiThread
 public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback {
 
-    public static final long NO_FRAME_PRODUCED = -1;
-
     private static final String TAG = "ScrollCaptureViewSupport";
 
     private static final boolean WAIT_FOR_ANIMATION = true;
 
     private final WeakReference<V> mWeakView;
     private final ScrollCaptureViewHelper<V> mViewHelper;
-    private ViewRenderer mRenderer;
-    private Handler mUiHandler;
+    private final ViewRenderer mRenderer;
     private boolean mStarted;
     private boolean mEnded;
 
     ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) {
         mWeakView = new WeakReference<>(containingView);
         mRenderer = new ViewRenderer();
-        mUiHandler = containingView.getHandler();
+        // TODO(b/177649144): provide access to color space from android.media.Image
         mViewHelper = viewHelper;
     }
 
-    // Base implementation of ScrollCaptureCallback
+    /** Based on ViewRootImpl#updateColorModeIfNeeded */
+    @ColorMode
+    private static int getColorMode(View containingView) {
+        Context context = containingView.getContext();
+        int colorMode = containingView.getViewRootImpl().mWindowAttributes.getColorMode();
+        if (!context.getResources().getConfiguration().isScreenWideColorGamut()) {
+            colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
+        }
+        return colorMode;
+    }
 
     @Override
-    public final void onScrollCaptureSearch(Consumer<Rect> onReady) {
+    public final void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
         mStarted = false;
         mEnded = false;
@@ -82,7 +96,11 @@
     }
 
     @Override
-    public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
+    public final void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal,
+            Runnable onReady) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
 
         mEnded = false;
@@ -99,18 +117,22 @@
     }
 
     @Override
-    public final void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect requestRect) {
+    public final void onScrollCaptureImageRequest(ScrollCaptureSession session,
+            CancellationSignal signal, Rect requestRect, Consumer<Rect> onComplete) {
+        if (signal.isCanceled()) {
+            return;
+        }
         V view = mWeakView.get();
         if (view == null || !view.isVisibleToUser()) {
             // Signal to the controller that we have a problem and can't continue.
-            session.notifyBufferSent(NO_FRAME_PRODUCED, new Rect());
+            onComplete.accept(new Rect());
             return;
         }
         // Ask the view to scroll as needed to bring this area into view.
         ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(),
                 requestRect);
         if (scrollResult.availableArea.isEmpty()) {
-            session.notifyBufferSent(NO_FRAME_PRODUCED, scrollResult.availableArea);
+            onComplete.accept(scrollResult.availableArea);
             return;
         }
         view.invalidate(); // don't wait for vsync
@@ -121,17 +143,13 @@
         viewCaptureArea.offset(0, -scrollResult.scrollDelta);
 
         if (WAIT_FOR_ANIMATION) {
-            Log.d(TAG, "render: delaying until animation");
             view.postOnAnimation(() ->  {
-                Log.d(TAG, "postOnAnimation(): rendering now");
-                long resultFrame = mRenderer.renderView(view, viewCaptureArea);
-                Log.d(TAG, "notifyBufferSent: " + scrollResult.availableArea);
-
-                session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+                mRenderer.renderView(view, viewCaptureArea);
+                onComplete.accept(new Rect(scrollResult.availableArea));
             });
         } else {
-            long resultFrame = mRenderer.renderView(view, viewCaptureArea);
-            session.notifyBufferSent(resultFrame, new Rect(scrollResult.availableArea));
+            mRenderer.renderView(view, viewCaptureArea);
+            onComplete.accept(new Rect(scrollResult.availableArea));
         }
     }
 
@@ -239,7 +257,7 @@
             mCaptureRenderNode.endRecording();
         }
 
-        public long renderView(View view, Rect sourceRect) {
+        public void renderView(View view, Rect sourceRect) {
             if (updateForView(view)) {
                 setupLighting(view);
             }
@@ -258,7 +276,7 @@
             switch (request.syncAndDraw()) {
                 case HardwareRenderer.SYNC_OK:
                 case HardwareRenderer.SYNC_REDRAW_REQUESTED:
-                    return frameNumber;
+                    return;
 
                 case HardwareRenderer.SYNC_FRAME_DROPPED:
                     Log.e(TAG, "syncAndDraw(): SYNC_FRAME_DROPPED !");
@@ -270,7 +288,6 @@
                     Log.e(TAG, "syncAndDraw(): SYNC_CONTEXT_IS_STOPPED !");
                     break;
             }
-            return NO_FRAME_PRODUCED;
         }
 
         public void trimMemory() {
@@ -289,5 +306,17 @@
             mTempMatrix.mapRect(mTempRectF);
             mTempRectF.round(outRect);
         }
+
+        public void setColorMode(@ColorMode int colorMode) {
+            mRenderer.setColorMode(colorMode);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "ScrollCaptureViewSupport{"
+                + "view=" + mWeakView.get()
+                + ", helper=" + mViewHelper
+                + '}';
     }
 }
diff --git a/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java b/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java
new file mode 100644
index 0000000..742bdfd
--- /dev/null
+++ b/core/java/com/android/internal/widget/NotificationVanishingFrameLayout.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.RemoteViews;
+
+/**
+ * This view will measure itself as having 0 size if all of its children are {@link #GONE}.
+ * Otherwise it acts like a normal {@link FrameLayout}.
+ */
+@RemoteViews.RemoteView
+public class NotificationVanishingFrameLayout extends FrameLayout {
+    public NotificationVanishingFrameLayout(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public NotificationVanishingFrameLayout(Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public NotificationVanishingFrameLayout(Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public NotificationVanishingFrameLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (allChildrenGone()) {
+            int zeroSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
+            super.onMeasure(zeroSpec, zeroSpec);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    private boolean allChildrenGone() {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child =  getChildAt(i);
+            if (child != null && child.getVisibility() != GONE) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
new file mode 100644
index 0000000..12629254
--- /dev/null
+++ b/core/java/com/android/server/OWNERS
@@ -0,0 +1 @@
+per-file SystemConfig.java = toddke@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b485f0f..2287900 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -211,6 +211,7 @@
                 "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                 "com_android_internal_os_KernelSingleUidTimeReader.cpp",
                 "com_android_internal_os_Zygote.cpp",
+                "com_android_internal_os_ZygoteCommandBuffer.cpp",
                 "com_android_internal_os_ZygoteInit.cpp",
                 "hwbinder/EphemeralStorage.cpp",
                 "fd_utils.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index dc77bba..ddd8613 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -198,6 +198,7 @@
 extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
 extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
+extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
 extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
@@ -1531,6 +1532,7 @@
         REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
         REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
         REG_JNI(register_com_android_internal_os_Zygote),
+        REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
         REG_JNI(register_com_android_internal_os_ZygoteInit),
         REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
         REG_JNI(register_android_hardware_Camera),
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e801725..c9062d8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -17,6 +17,8 @@
 #define LOG_TAG "Zygote"
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 
+#include "com_android_internal_os_Zygote.h"
+
 #include <async_safe/log.h>
 
 // sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
@@ -91,19 +93,6 @@
 
 #include "nativebridge/native_bridge.h"
 
-/* Functions in the callchain during the fork shall not be protected with
-   Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
-#ifdef __ARM_FEATURE_PAC_DEFAULT
-#ifdef __ARM_FEATURE_BTI_DEFAULT
-#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
-#else
-#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
-#endif /* __ARM_FEATURE_BTI_DEFAULT */
-#else /* !__ARM_FEATURE_PAC_DEFAULT */
-#define NO_PAC_FUNC
-#endif /* __ARM_FEATURE_PAC_DEFAULT */
-
-
 namespace {
 
 // TODO (chriswailes): Add a function to initialize native Zygote data.
@@ -118,8 +107,7 @@
 using android::base::WriteStringToFile;
 using android::base::GetBoolProperty;
 
-#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
-                              append(StringPrintf(__VA_ARGS__))
+using android::zygote::ZygoteFailure;
 
 // This type is duplicated in fd_utils.h
 typedef const std::function<void(std::string)>& fail_fn_t;
@@ -214,7 +202,7 @@
   static constexpr EntryStorage INVALID_ENTRY_VALUE = {-1, -1};
 
   std::atomic<EntryStorage> mStorage;
-  static_assert(decltype(mStorage)::is_always_lock_free);
+  static_assert(decltype(mStorage)::is_always_lock_free);  // Accessed from signal handler.
 
  public:
   constexpr UsapTableEntry() : mStorage(INVALID_ENTRY_VALUE) {}
@@ -917,36 +905,6 @@
 }
 
 /**
- * A failure function used to report fatal errors to the managed runtime.  This
- * function is often curried with the process name information and then passed
- * to called functions.
- *
- * @param env  Managed runtime environment
- * @param process_name  A native representation of the process name
- * @param managed_process_name  A managed representation of the process name
- * @param msg  The error message to be reported
- */
-[[noreturn]]
-static void ZygoteFailure(JNIEnv* env,
-                          const char* process_name,
-                          jstring managed_process_name,
-                          const std::string& msg) {
-  std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
-  if (managed_process_name != nullptr) {
-    scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
-    if (scoped_managed_process_name_ptr->c_str() != nullptr) {
-      process_name = scoped_managed_process_name_ptr->c_str();
-    }
-  }
-
-  const std::string& error_msg =
-      (process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str());
-
-  env->FatalError(error_msg.c_str());
-  __builtin_unreachable();
-}
-
-/**
  * A helper method for converting managed strings to native strings.  A fatal
  * error is generated if a problem is encountered in extracting a non-null
  * string.
@@ -1073,86 +1031,6 @@
 #endif
 }
 
-// Utility routine to fork a process from the zygote.
-NO_PAC_FUNC
-static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
-                        const std::vector<int>& fds_to_close,
-                        const std::vector<int>& fds_to_ignore,
-                        bool is_priority_fork) {
-  SetSignalHandlers();
-
-  // Curry a failure function.
-  auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
-                           nullptr, _1);
-
-  // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
-  // log, which would result in the logging FDs we close being reopened.
-  // This would cause failures because the FDs are not allowlisted.
-  //
-  // Note that the zygote process is single threaded at this point.
-  BlockSignal(SIGCHLD, fail_fn);
-
-  // Close any logging related FDs before we start evaluating the list of
-  // file descriptors.
-  __android_log_close();
-  AStatsSocket_close();
-
-  // If this is the first fork for this zygote, create the open FD table.  If
-  // it isn't, we just need to check whether the list of open files has changed
-  // (and it shouldn't in the normal case).
-  if (gOpenFdTable == nullptr) {
-    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
-  } else {
-    gOpenFdTable->Restat(fds_to_ignore, fail_fn);
-  }
-
-  android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
-
-  // Purge unused native memory in an attempt to reduce the amount of false
-  // sharing with the child process.  By reducing the size of the libc_malloc
-  // region shared with the child process we reduce the number of pages that
-  // transition to the private-dirty state when malloc adjusts the meta-data
-  // on each of the pages it is managing after the fork.
-  mallopt(M_PURGE, 0);
-
-  pid_t pid = fork();
-
-  if (pid == 0) {
-    if (is_priority_fork) {
-      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
-    } else {
-      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
-    }
-
-    // The child process.
-    PAuthKeyChange(env);
-    PreApplicationInit();
-
-    // Clean up any descriptors which must be closed immediately
-    DetachDescriptors(env, fds_to_close, fail_fn);
-
-    // Invalidate the entries in the USAP table.
-    ClearUsapTable();
-
-    // Re-open all remaining open file descriptors so that they aren't shared
-    // with the zygote across a fork.
-    gOpenFdTable->ReopenOrDetach(fail_fn);
-
-    // Turn fdsan back on.
-    android_fdsan_set_error_level(fdsan_error_level);
-
-    // Reset the fd to the unsolicited zygote socket
-    gSystemServerSocketFd = -1;
-  } else {
-    ALOGD("Forked child process %d", pid);
-  }
-
-  // We blocked SIGCHLD prior to a fork, we unblock it here.
-  UnblockSignal(SIGCHLD, fail_fn);
-
-  return pid;
-}
-
 // Create an app data directory over tmpfs overlayed CE / DE storage, and bind mount it
 // from the actual app data directory in data mirror.
 static bool createAndMountAppData(std::string_view package_name,
@@ -1973,9 +1851,10 @@
   static int sUsapTableInsertIndex = 0;
 
   int search_index = sUsapTableInsertIndex;
-
   do {
     if (gUsapTable[search_index].SetIfInvalid(usap_pid, read_pipe_fd)) {
+      ++gUsapPoolCount;
+
       // Start our next search right after where we finished this one.
       sUsapTableInsertIndex = (search_index + 1) % gUsapTable.size();
 
@@ -1993,7 +1872,7 @@
 /**
  * Invalidates the entry in the USAPTable corresponding to the provided
  * process ID if it is present.  If an entry was removed the USAP pool
- * count is decremented.
+ * count is decremented. May be called from signal handler.
  *
  * @param usap_pid  Process ID of the USAP entry to invalidate
  * @return True if an entry was invalidated; false otherwise
@@ -2069,6 +1948,121 @@
 
 namespace android {
 
+/**
+ * A failure function used to report fatal errors to the managed runtime.  This
+ * function is often curried with the process name information and then passed
+ * to called functions.
+ *
+ * @param env  Managed runtime environment
+ * @param process_name  A native representation of the process name
+ * @param managed_process_name  A managed representation of the process name
+ * @param msg  The error message to be reported
+ */
+[[noreturn]]
+void zygote::ZygoteFailure(JNIEnv* env,
+                           const char* process_name,
+                           jstring managed_process_name,
+                           const std::string& msg) {
+  std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
+  if (managed_process_name != nullptr) {
+    scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
+    if (scoped_managed_process_name_ptr->c_str() != nullptr) {
+      process_name = scoped_managed_process_name_ptr->c_str();
+    }
+  }
+
+  const std::string& error_msg =
+      (process_name == nullptr || process_name[0] == '\0') ?
+      msg : StringPrintf("(%s) %s", process_name, msg.c_str());
+
+  env->FatalError(error_msg.c_str());
+  __builtin_unreachable();
+}
+
+// Utility routine to fork a process from the zygote.
+NO_PAC_FUNC
+pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
+                         const std::vector<int>& fds_to_close,
+                         const std::vector<int>& fds_to_ignore,
+                         bool is_priority_fork,
+                         bool purge) {
+  SetSignalHandlers();
+
+  // Curry a failure function.
+  auto fail_fn = std::bind(zygote::ZygoteFailure, env,
+                           is_system_server ? "system_server" : "zygote",
+                           nullptr, _1);
+
+  // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
+  // log, which would result in the logging FDs we close being reopened.
+  // This would cause failures because the FDs are not allowlisted.
+  //
+  // Note that the zygote process is single threaded at this point.
+  BlockSignal(SIGCHLD, fail_fn);
+
+  // Close any logging related FDs before we start evaluating the list of
+  // file descriptors.
+  __android_log_close();
+  AStatsSocket_close();
+
+  // If this is the first fork for this zygote, create the open FD table.  If
+  // it isn't, we just need to check whether the list of open files has changed
+  // (and it shouldn't in the normal case).
+  if (gOpenFdTable == nullptr) {
+    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
+  } else {
+    gOpenFdTable->Restat(fds_to_ignore, fail_fn);
+  }
+
+  android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
+
+  if (purge) {
+    // Purge unused native memory in an attempt to reduce the amount of false
+    // sharing with the child process.  By reducing the size of the libc_malloc
+    // region shared with the child process we reduce the number of pages that
+    // transition to the private-dirty state when malloc adjusts the meta-data
+    // on each of the pages it is managing after the fork.
+    mallopt(M_PURGE, 0);
+  }
+
+  pid_t pid = fork();
+
+  if (pid == 0) {
+    if (is_priority_fork) {
+      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+    } else {
+      setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
+    }
+
+    // The child process.
+    PAuthKeyChange(env);
+    PreApplicationInit();
+
+    // Clean up any descriptors which must be closed immediately
+    DetachDescriptors(env, fds_to_close, fail_fn);
+
+    // Invalidate the entries in the USAP table.
+    ClearUsapTable();
+
+    // Re-open all remaining open file descriptors so that they aren't shared
+    // with the zygote across a fork.
+    gOpenFdTable->ReopenOrDetach(fail_fn);
+
+    // Turn fdsan back on.
+    android_fdsan_set_error_level(fdsan_error_level);
+
+    // Reset the fd to the unsolicited zygote socket
+    gSystemServerSocketFd = -1;
+  } else {
+    ALOGD("Forked child process %d", pid);
+  }
+
+  // We blocked SIGCHLD prior to a fork, we unblock it here.
+  UnblockSignal(SIGCHLD, fail_fn);
+
+  return pid;
+}
+
 static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
   PreApplicationInit();
 }
@@ -2085,7 +2079,8 @@
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
     if (UNLIKELY(managed_fds_to_close == nullptr)) {
-      ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector.");
+      zygote::ZygoteFailure(env, "zygote", nice_name,
+                            "Zygote received a null fds_to_close vector.");
     }
 
     std::vector<int> fds_to_close =
@@ -2111,7 +2106,7 @@
         fds_to_ignore.push_back(gSystemServerSocketFd);
     }
 
-    pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
+    pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
 
     if (pid == 0) {
       SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
@@ -2146,10 +2141,10 @@
       fds_to_ignore.push_back(gSystemServerSocketFd);
   }
 
-  pid_t pid = ForkCommon(env, true,
-                         fds_to_close,
-                         fds_to_ignore,
-                         true);
+  pid_t pid = zygote::ForkCommon(env, true,
+                                 fds_to_close,
+                                 fds_to_ignore,
+                                 true);
   if (pid == 0) {
       // System server prcoess does not need data isolation so no need to
       // know pkg_data_info_list.
@@ -2189,58 +2184,74 @@
  * ensuring proper file descriptor hygiene.
  *
  * @param env  Managed runtime environment
- * @param read_pipe_fd  The read FD for the USAP reporting pipe.  Manually closed by blastlas
- * in managed code.
+ * @param read_pipe_fd  The read FD for the USAP reporting pipe.  Manually closed by the child
+ * in managed code. -1 indicates none.
  * @param write_pipe_fd  The write FD for the USAP reporting pipe.  Manually closed by the
- * zygote in managed code.
+ * zygote in managed code. -1 indicates none.
  * @param managed_session_socket_fds  A list of anonymous session sockets that must be ignored by
  * the FD hygiene code and automatically "closed" in the new USAP.
+ * @param args_known Arguments for specialization are available; no need to read from a socket
  * @param is_priority_fork  Controls the nice level assigned to the newly created process
- * @return
+ * @return child pid in the parent, 0 in the child
  */
 NO_PAC_FUNC
-static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
-                                                          jclass,
-                                                          jint read_pipe_fd,
-                                                          jint write_pipe_fd,
-                                                          jintArray managed_session_socket_fds,
-                                                          jboolean is_priority_fork) {
-  std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
-                   fds_to_ignore(fds_to_close);
-
+static jint com_android_internal_os_Zygote_nativeForkApp(JNIEnv* env,
+                                                         jclass,
+                                                         jint read_pipe_fd,
+                                                         jint write_pipe_fd,
+                                                         jintArray managed_session_socket_fds,
+                                                         jboolean args_known,
+                                                         jboolean is_priority_fork) {
   std::vector<int> session_socket_fds =
       ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds)
           .value_or(std::vector<int>());
+  return zygote::forkApp(env, read_pipe_fd, write_pipe_fd, session_socket_fds,
+                            args_known == JNI_TRUE, is_priority_fork == JNI_TRUE, true);
+}
 
-  // The USAP Pool Event FD is created during the initialization of the
-  // USAP pool and should always be valid here.
+NO_PAC_FUNC
+int zygote::forkApp(JNIEnv* env,
+                    int read_pipe_fd,
+                    int write_pipe_fd,
+                    const std::vector<int>& session_socket_fds,
+                    bool args_known,
+                    bool is_priority_fork,
+                    bool purge) {
+
+  std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
+                   fds_to_ignore(fds_to_close);
 
   fds_to_close.push_back(gZygoteSocketFD);
-  fds_to_close.push_back(gUsapPoolEventFD);
-  fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
   if (gSystemServerSocketFd != -1) {
       fds_to_close.push_back(gSystemServerSocketFd);
   }
+  if (args_known) {
+      fds_to_close.push_back(gUsapPoolSocketFD);
+  }
+  fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
 
-  fds_to_ignore.push_back(gZygoteSocketFD);
   fds_to_ignore.push_back(gUsapPoolSocketFD);
-  fds_to_ignore.push_back(gUsapPoolEventFD);
-  fds_to_ignore.push_back(read_pipe_fd);
-  fds_to_ignore.push_back(write_pipe_fd);
+  fds_to_ignore.push_back(gZygoteSocketFD);
+  if (read_pipe_fd != -1) {
+      fds_to_ignore.push_back(read_pipe_fd);
+  }
+  if (write_pipe_fd != -1) {
+      fds_to_ignore.push_back(write_pipe_fd);
+  }
   fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+  if (gUsapPoolEventFD != -1) {
+      fds_to_close.push_back(gUsapPoolEventFD);
+      fds_to_ignore.push_back(gUsapPoolEventFD);
+  }
   if (gSystemServerSocketFd != -1) {
+      if (args_known) {
+          fds_to_close.push_back(gSystemServerSocketFd);
+      }
       fds_to_ignore.push_back(gSystemServerSocketFd);
   }
-
-  pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
-                              is_priority_fork == JNI_TRUE);
-
-  if (usap_pid != 0) {
-    ++gUsapPoolCount;
-    AddUsapTableEntry(usap_pid, read_pipe_fd);
-  }
-
-  return usap_pid;
+  return zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close,
+                            fds_to_ignore, is_priority_fork == JNI_TRUE, purge);
 }
 
 static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
@@ -2354,7 +2365,7 @@
    */
 
   if (!SetTaskProfiles(0, {})) {
-    ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
+    zygote::ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
   }
 }
 
@@ -2372,15 +2383,21 @@
   return managed_usap_fds;
 }
 
+/*
+ * Add the given pid and file descriptor to the Usap table. CriticalNative method.
+ */
+static void com_android_internal_os_Zygote_nativeAddUsapTableEntry(jint pid, jint read_pipe_fd) {
+  AddUsapTableEntry(pid, read_pipe_fd);
+}
+
 /**
- * A JNI wrapper around RemoveUsapTableEntry.
+ * A JNI wrapper around RemoveUsapTableEntry. CriticalNative method.
  *
  * @param env  Managed runtime environment
  * @param usap_pid  Process ID of the USAP entry to invalidate
  * @return  True if an entry was invalidated; false otherwise.
  */
-static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(JNIEnv* env, jclass,
-                                                                          jint usap_pid) {
+static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(jint usap_pid) {
   return RemoveUsapTableEntry(usap_pid);
 }
 
@@ -2395,7 +2412,8 @@
 static jint com_android_internal_os_Zygote_nativeGetUsapPoolEventFD(JNIEnv* env, jclass) {
   if (gUsapPoolEventFD == -1) {
     if ((gUsapPoolEventFD = eventfd(0, 0)) == -1) {
-      ZygoteFailure(env, "zygote", nullptr, StringPrintf("Unable to create eventfd: %s", strerror(errno)));
+      zygote::ZygoteFailure(env, "zygote", nullptr,
+                            StringPrintf("Unable to create eventfd: %s", strerror(errno)));
     }
   }
 
@@ -2438,12 +2456,12 @@
 }
 
 static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
-  auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
+  auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1);
   BlockSignal(SIGTERM, fail_fn);
 }
 
 static void com_android_internal_os_Zygote_nativeUnblockSigTerm(JNIEnv* env, jclass) {
-  auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
+  auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1);
   UnblockSignal(SIGTERM, fail_fn);
 }
 
@@ -2549,7 +2567,10 @@
          (void*)com_android_internal_os_Zygote_nativePreApplicationInit},
         {"nativeInstallSeccompUidGidFilter", "(II)V",
          (void*)com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter},
-        {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
+        {"nativeForkApp", "(II[IZZ)I", (void*)com_android_internal_os_Zygote_nativeForkApp},
+        // @CriticalNative
+        {"nativeAddUsapTableEntry", "(II)V",
+         (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry},
         {"nativeSpecializeAppProcess",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
          "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
@@ -2558,6 +2579,10 @@
          (void*)com_android_internal_os_Zygote_nativeInitNativeState},
         {"nativeGetUsapPipeFDs", "()[I",
          (void*)com_android_internal_os_Zygote_nativeGetUsapPipeFDs},
+        // @CriticalNative
+        {"nativeAddUsapTableEntry", "(II)V",
+         (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry},
+        // @CriticalNative
         {"nativeRemoveUsapTableEntry", "(I)Z",
          (void*)com_android_internal_os_Zygote_nativeRemoveUsapTableEntry},
         {"nativeGetUsapPoolEventFD", "()I",
diff --git a/core/jni/com_android_internal_os_Zygote.h b/core/jni/com_android_internal_os_Zygote.h
new file mode 100644
index 0000000..d2da914
--- /dev/null
+++ b/core/jni/com_android_internal_os_Zygote.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _COM_ANDROID_INTERNAL_OS_ZYGOTE_H
+#define _COM_ANDROID_INTERNAL_OS_ZYGOTE_H
+
+#define LOG_TAG "Zygote"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+/* Functions in the callchain during the fork shall not be protected with
+   Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
+#ifdef __ARM_FEATURE_PAC_DEFAULT
+#ifdef __ARM_FEATURE_BTI_DEFAULT
+#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
+#else
+#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
+#endif /* __ARM_FEATURE_BTI_DEFAULT */
+#else /* !__ARM_FEATURE_PAC_DEFAULT */
+#define NO_PAC_FUNC
+#endif /* __ARM_FEATURE_PAC_DEFAULT */
+
+#include <jni.h>
+#include <vector>
+#include <android-base/stringprintf.h>
+
+#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
+                              append(StringPrintf(__VA_ARGS__))
+
+namespace android {
+namespace zygote {
+
+NO_PAC_FUNC
+pid_t ForkCommon(JNIEnv* env,bool is_system_server,
+                 const std::vector<int>& fds_to_close,
+                 const std::vector<int>& fds_to_ignore,
+                 bool is_priority_fork,
+                 bool purge = true);
+
+/**
+ * Fork a process. The pipe fds are used for usap communication, or -1 in
+ * other cases. Session_socket_fds are FDs used for zygote communication that must be dealt
+ * with hygienically, but are not otherwise used here. Args_known indicates that the process
+ * will be immediately specialized with arguments that are already known, so no usap
+ * communication is required. Is_priority_fork should be true if this is on the app startup
+ * critical path. Purge specifies that unused pages should be purged before the fork.
+ */
+NO_PAC_FUNC
+int forkApp(JNIEnv* env,
+            int read_pipe_fd,
+            int write_pipe_fd,
+            const std::vector<int>& session_socket_fds,
+            bool args_known,
+            bool is_priority_fork,
+            bool purge);
+
+[[noreturn]]
+void ZygoteFailure(JNIEnv* env,
+                   const char* process_name,
+                   jstring managed_process_name,
+                   const std::string& msg);
+
+}  // namespace zygote
+}  // namespace android
+
+#endif // _COM_ANDROID_INTERNAL_OS_ZYGOTE_
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
new file mode 100644
index 0000000..011e8f8
--- /dev/null
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -0,0 +1,512 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "com_android_internal_os_Zygote.h"
+
+#include <algorithm>
+#include <android-base/logging.h>
+#include <async_safe/log.h>
+#include <cctype>
+#include <chrono>
+#include <core_jni_helpers.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <optional>
+#include <poll.h>
+#include <unistd.h>
+#include <utility>
+#include <utils/misc.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <vector>
+
+namespace android {
+
+using namespace std::placeholders;
+using android::base::StringPrintf;
+using android::zygote::ZygoteFailure;
+
+// WARNING: Knows a little about the wire protocol used to communicate with Zygote.
+// TODO: Fix error handling.
+
+constexpr size_t MAX_COMMAND_BYTES = 12200;
+constexpr size_t NICE_NAME_BYTES = 50;
+
+// A buffer optionally bundled with a file descriptor from which we can fill it.
+// Does not own the file descriptor; destroying a NativeCommandBuffer does not
+// close the descriptor.
+class NativeCommandBuffer {
+ public:
+  NativeCommandBuffer(int sourceFd): mEnd(0), mNext(0), mLinesLeft(0), mFd(sourceFd) {}
+
+  // Read mNext line from mFd, filling mBuffer from file descriptor, as needed.
+  // Return a pair of pointers pointing to the first character, and one past the
+  // mEnd of the line, i.e. at the newline. Returns nothing on failure.
+  template<class FailFn>
+  std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) {
+    char* result = mBuffer + mNext;
+    while (true) {
+      if (mNext == mEnd) {
+        if (mEnd == MAX_COMMAND_BYTES) {
+          return {};
+        }
+        if (mFd == -1) {
+          fail_fn("ZygoteCommandBuffer.readLine attempted to read from mFd -1");
+        }
+        ssize_t nread = TEMP_FAILURE_RETRY(read(mFd, mBuffer + mEnd, MAX_COMMAND_BYTES - mEnd));
+        if (nread <= 0) {
+          if (nread == 0) {
+            return {};
+          }
+          fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
+        } else if (nread == MAX_COMMAND_BYTES - mEnd) {
+          // This is pessimistic by one character, but close enough.
+          fail_fn("ZygoteCommandBuffer overflowed: command too long");
+        }
+        mEnd += nread;
+      }
+      // UTF-8 does not allow newline to occur as part of a multibyte character.
+      char* nl = static_cast<char *>(memchr(mBuffer + mNext, '\n', mEnd - mNext));
+      if (nl == nullptr) {
+        mNext = mEnd;
+      } else {
+        mNext = nl - mBuffer + 1;
+        if (--mLinesLeft < 0) {
+          fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command");
+        }
+        return std::make_pair(result, nl);
+      }
+    }
+  }
+
+  void reset() {
+    mNext = 0;
+  }
+
+  // Make sure the current command is fully buffered, without reading past the current command.
+  template<class FailFn>
+  void readAllLines(FailFn fail_fn) {
+     while (mLinesLeft > 0) {
+       readLine(fail_fn);
+    }
+  }
+
+  void clear() {
+    // Don't bother to actually clear the buffer; it'll be unmapped in the child anyway.
+    reset();
+    mNiceName[0] = '\0';
+    mEnd = 0;
+  }
+
+  // Insert line into the mBuffer. Checks that the mBuffer is not associated with an mFd.
+  // Implicitly adds newline separators. Allows mBuffer contents to be explicitly set.
+  void insert(const char* line, size_t lineLen) {
+    DCHECK(mFd == -1);
+    CHECK(mEnd + lineLen < MAX_COMMAND_BYTES);
+    strncpy(mBuffer + mEnd, line, lineLen);
+    mBuffer[mEnd + lineLen] = '\n';
+    mEnd += lineLen + 1;
+  }
+
+  // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer
+  // positioned at the beginning of first argument. Return 0 on EOF.
+  template<class FailFn>
+  int getCount(FailFn fail_fn) {
+    mLinesLeft = 1;
+    auto line = readLine(fail_fn);
+    if (!line.has_value()) {
+      return 0;
+    }
+    char* countString = line.value().first;  // Newline terminated.
+    long nArgs = atol(countString);
+    if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
+      fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
+    }
+    mLinesLeft = nArgs;
+    return static_cast<int>(nArgs);
+  }
+
+  // Is the mBuffer a simple fork command?
+  // We disallow request to wrap the child process, child zygotes, anything that
+  // mentions capabilities or requests uid < minUid.
+  // We insist that --setuid and --setgid arguments are explicitly included and that the
+  // command starts with --runtime-args.
+  // Assumes we are positioned at the beginning of the command after the argument count,
+  // and leaves the position at some indeterminate position in the buffer.
+  // As a side effect, this sets mNiceName to a non-empty string, if possible.
+  template<class FailFn>
+  bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
+    if (mLinesLeft <= 0 || mLinesLeft  >= MAX_COMMAND_BYTES / 2) {
+      return false;
+    }
+    static const char* RUNTIME_ARGS = "--runtime-args";
+    static const char* INVOKE_WITH = "--invoke-with";
+    static const char* CHILD_ZYGOTE = "--start-child-zygote";
+    static const char* SETUID = "--setuid=";
+    static const char* SETGID = "--setgid=";
+    static const char* CAPABILITIES = "--capabilities";
+    static const char* NICE_NAME = "--nice-name=";
+    static const size_t RA_LENGTH = strlen(RUNTIME_ARGS);
+    static const size_t IW_LENGTH = strlen(INVOKE_WITH);
+    static const size_t CZ_LENGTH = strlen(CHILD_ZYGOTE);
+    static const size_t SU_LENGTH = strlen(SETUID);
+    static const size_t SG_LENGTH = strlen(SETGID);
+    static const size_t CA_LENGTH = strlen(CAPABILITIES);
+    static const size_t NN_LENGTH = strlen(NICE_NAME);
+
+    bool saw_setuid = false, saw_setgid = false;
+    bool saw_runtime_args = false;
+
+    while (mLinesLeft > 0) {
+      auto read_result = readLine(fail_fn);
+      if (!read_result.has_value()) {
+        return false;
+      }
+      auto [arg_start, arg_end] = read_result.value();
+      if (arg_end - arg_start == RA_LENGTH
+          && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
+        saw_runtime_args = true;
+        continue;
+      }
+      if (arg_end - arg_start >= NN_LENGTH
+          && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
+        size_t name_len = arg_end - (arg_start + NN_LENGTH);
+        size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
+        memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
+        mNiceName[copy_len] = '\0';
+        continue;
+      }
+      if (arg_end - arg_start == IW_LENGTH
+          && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
+        // This also removes the need for invoke-with security checks here.
+        return false;
+      }
+      if (arg_end - arg_start == CZ_LENGTH
+          && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
+        return false;
+      }
+      if (arg_end - arg_start >= CA_LENGTH
+          && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
+        return false;
+      }
+      if (arg_end - arg_start >= SU_LENGTH
+          && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
+        int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
+        if (uid < minUid) {
+          return false;
+        }
+        saw_setuid = true;
+        continue;
+      }
+      if (arg_end - arg_start >= SG_LENGTH
+          && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
+        int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
+        if (gid == -1) {
+          return false;
+        }
+        saw_setgid = true;
+      }
+    }
+    return saw_runtime_args && saw_setuid && saw_setgid;
+  }
+
+  void setFd(int new_fd) {
+    mFd = new_fd;
+  }
+
+  int getFd() const {
+    return mFd;
+  }
+
+  const char* niceNameAddr() const {
+    return mNiceName;
+  }
+
+  // Debug only:
+  void logState() const {
+    ALOGD("mbuffer starts with %c%c, nice name is %s, "
+          "mEnd = %u, mNext = %u, mLinesLeft = %d, mFd = %d",
+          mBuffer[0], (mBuffer[1] == '\n' ? ' ' : mBuffer[1]),
+          niceNameAddr(),
+          static_cast<unsigned>(mEnd), static_cast<unsigned>(mNext),
+          static_cast<int>(mLinesLeft), mFd);
+  }
+
+ private:
+  // Picky version of atoi(). No sign or unexpected characters allowed. Return -1 on failure.
+  static int digitsVal(char* start, char* end) {
+    int result = 0;
+    if (end - start > 6) {
+      return -1;
+    }
+    for (char* dp = start; dp < end; ++dp) {
+      if (*dp < '0' || *dp > '9') {
+        ALOGW("Argument failed integer format check");
+        return -1;
+      }
+      result = 10 * result + (*dp - '0');
+    }
+    return result;
+  }
+
+  uint32_t mEnd;  // Index of first empty byte in the mBuffer.
+  uint32_t mNext;  // Index of first character past last line returned by readLine.
+  int32_t mLinesLeft;  // Lines in current command that haven't yet been read.
+  int mFd;  // Open file descriptor from which we can read more. -1 if none.
+  char mNiceName[NICE_NAME_BYTES];
+  char mBuffer[MAX_COMMAND_BYTES];
+};
+
+static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
+
+static int buffersAllocd(0);
+
+// Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
+// so that only one buffer exists at a time.
+jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, jclass, jint fd) {
+  CHECK(buffersAllocd == 0);
+  ++buffersAllocd;
+  // MMap explicitly to get it page aligned.
+  void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
+                         MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+  // Currently we mmap and unmap one for every request handled by the Java code.
+  // That could be improved, but unclear it matters.
+  if (bufferMem == MAP_FAILED) {
+    ZygoteFailure(env, nullptr, nullptr, "Failed to map argument buffer");
+  }
+  return (jlong) new(bufferMem) NativeCommandBuffer(fd);
+}
+
+// Delete native command buffer.
+void com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv* env, jclass,
+                                                                  jlong j_buffer) {
+  CHECK(buffersAllocd == 1);
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  n_buffer->~NativeCommandBuffer();
+  if (munmap(n_buffer, sizeof(NativeCommandBuffer)) != 0) {
+    ZygoteFailure(env, nullptr, nullptr, "Failed to unmap argument buffer");
+  }
+  --buffersAllocd;
+}
+
+// Clear the buffer, read the line containing the count, and return the count.
+jint com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv* env, jclass,
+                                                                jlong j_buffer) {
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  auto fail_fn = std::bind(ZygoteFailure, env, nullptr, nullptr, _1);
+  return n_buffer->getCount(fail_fn);
+}
+
+// Explicitly insert a string as the last line (argument) of the buffer.
+void com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv* env, jclass, jlong j_buffer,
+                                                        jstring line) {
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  size_t lineLen = static_cast<size_t>(env->GetStringUTFLength(line));
+  const char* cstring = env->GetStringUTFChars(line, NULL);
+  n_buffer->insert(cstring, lineLen);
+  env->ReleaseStringUTFChars(line, cstring);
+}
+
+// Read a line from the buffer, refilling as necessary.
+jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, jclass,
+                                                                  jlong j_buffer) {
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
+  auto line = n_buffer->readLine(fail_fn);
+  if (!line.has_value()) {
+    fail_fn("Incomplete zygote command");
+  }
+  auto [cresult, endp] = line.value();
+  // OK to temporarily clobber the buffer, since this is not thread safe, and we're modifying
+  // the buffer anyway.
+  *endp = '\0';
+  jstring result = env->NewStringUTF(cresult);
+  *endp = '\n';
+  return result;
+}
+
+// Read all lines from the current command into the buffer, and then reset the buffer, so
+// we will start reading again at the beginning of the command, starting with the argument
+// count. And we don't need access to the fd to do so.
+void com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv* env, jclass,
+                                                                         jlong j_buffer) {
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
+  n_buffer->readAllLines(fail_fn);
+  n_buffer->reset();
+}
+
+// Fork a child as specified by the current command buffer, and refill the command
+// buffer from the given socket. So long as the result is another simple fork command,
+// repeat this process.
+// It must contain a fork command, which is currently restricted not to fork another
+// zygote or involve a wrapper process.
+// The initial buffer should be partially or entirely read; we read it fully and reset it.
+// When we return, the buffer contains the command we couldn't handle, and has been reset().
+// We return false in the parent when we see a command we didn't understand, and thus the
+// command in the buffer still needs to be executed.
+// We return true in each child.
+// We only process fork commands if the peer uid matches expected_uid.
+// For every fork command after the first, we check that the requested uid is at
+// least minUid.
+NO_PAC_FUNC
+jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
+            JNIEnv* env,
+            jclass,
+            jlong j_buffer,
+            jint zygote_socket_fd,
+            jint expected_uid,
+            jint minUid,
+            jstring managed_nice_name) {
+
+  NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+  int session_socket = n_buffer->getFd();
+  std::vector<int> session_socket_fds {session_socket};
+  auto fail_fn_1 = std::bind(ZygoteFailure, env, static_cast<const char*>(nullptr),
+                             static_cast<jstring>(managed_nice_name), _1);
+  // This binds to the nice name address; the actual names are updated by isSimpleForkCommand:
+  auto fail_fn_n = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(),
+                             static_cast<jstring>(nullptr), _1);
+  auto fail_fn_z = std::bind(ZygoteFailure, env, "zygote", nullptr, _1);
+
+  struct pollfd fd_structs[2];
+  static const int ZYGOTE_IDX = 0;
+  static const int SESSION_IDX = 1;
+  fd_structs[ZYGOTE_IDX].fd = zygote_socket_fd;
+  fd_structs[ZYGOTE_IDX].events = POLLIN;
+  fd_structs[SESSION_IDX].fd = session_socket;
+  fd_structs[SESSION_IDX].events = POLLIN;
+
+  struct timeval timeout;
+  socklen_t timeout_size = sizeof timeout;
+  if (getsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &timeout_size) != 0) {
+    fail_fn_z("Failed to retrieve session socket timeout");
+  }
+
+  struct ucred credentials;
+  socklen_t cred_size = sizeof credentials;
+  if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
+      || cred_size != sizeof credentials) {
+    fail_fn_1("ForkMany failed to get initial credentials, %s", strerror(errno));
+  }
+
+  bool first_time = true;
+  do {
+    if (credentials.uid != expected_uid) {
+      return JNI_FALSE;
+    }
+    n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
+    n_buffer->reset();
+    int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
+                              /*args_known=*/ true, /*is_priority_fork=*/ true,
+                              /*purge=*/ first_time);
+    if (pid == 0) {
+      return JNI_TRUE;
+    }
+    // We're in the parent. Write big-endian pid, followed by a boolean.
+    char pid_buf[5];
+    int tmp_pid = pid;
+    for (int i = 3; i >= 0; --i) {
+      pid_buf[i] = tmp_pid & 0xff;
+      tmp_pid >>= 8;
+    }
+    pid_buf[4] = 0;  // Process is not wrapped.
+    int res = write(session_socket, pid_buf, 5);
+    if (res != 5) {
+      if (res == -1) {
+        (first_time ? fail_fn_1 : fail_fn_n)
+            (CREATE_ERROR("Pid write error %d: %s", errno, strerror(errno)));
+      } else {
+        (first_time ? fail_fn_1 : fail_fn_n)
+            (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res));
+      }
+    }
+    // Clear buffer and get count from next command.
+    n_buffer->clear();
+    for (;;) {
+      // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
+      int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
+      if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
+        if (n_buffer->getCount(fail_fn_z) != 0) {
+          break;
+        }  // else disconnected;
+      } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
+        fail_fn_z(
+            CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
+      }
+      // We've now seen either a disconnect or connect request.
+      close(session_socket);
+      int new_fd = accept(zygote_socket_fd, nullptr, nullptr);
+      if (new_fd == -1) {
+        fail_fn_z("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno));
+      }
+      if (new_fd != session_socket) {
+          // Move new_fd back to the old value, so that we don't have to change Java-level data
+          // structures to reflect a change. This implicitly closes the old one.
+          if (dup2(new_fd, session_socket) != session_socket) {
+            fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
+                                   new_fd, session_socket, strerror(errno)));
+          }
+          close(new_fd);
+      }
+      // If we ever return, we effectively reuse the old Java ZygoteConnection.
+      // None of its state needs to change.
+      if (setsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, timeout_size) != 0) {
+        fail_fn_z(CREATE_ERROR("Failed to set receive timeout for socket %d: %s",
+                               session_socket, strerror(errno)));
+      }
+      if (setsockopt(session_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, timeout_size) != 0) {
+        fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
+                               session_socket, strerror(errno)));
+      }
+      if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
+        fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
+      }
+      if (cred_size != sizeof credentials) {
+        fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
+                               cred_size, static_cast<int>(sizeof credentials)));
+      }
+    }
+    first_time = false;
+  } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
+  ALOGW("forkRepeatedly terminated due to non-simple command");
+  n_buffer->logState();
+  n_buffer->reset();
+  return JNI_FALSE;
+}
+
+#define METHOD_NAME(m) com_android_internal_os_ZygoteCommandBuffer_ ## m
+
+static const JNINativeMethod gMethods[] = {
+        {"getNativeBuffer", "(I)J", (void *) METHOD_NAME(getNativeBuffer)},
+        {"freeNativeBuffer", "(J)V", (void *) METHOD_NAME(freeNativeBuffer)},
+        {"insert", "(JLjava/lang/String;)V", (void *) METHOD_NAME(insert)},
+        {"nativeNextArg", "(J)Ljava/lang/String;", (void *) METHOD_NAME(nativeNextArg)},
+        {"nativeReadFullyAndReset", "(J)V", (void *) METHOD_NAME(nativeReadFullyAndReset)},
+        {"nativeGetCount", "(J)I", (void *) METHOD_NAME(nativeGetCount)},
+        {"nativeForkRepeatedly", "(JIIILjava/lang/String;)Z",
+          (void *) METHOD_NAME(nativeForkRepeatedly)},
+};
+
+int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv* env) {
+  return RegisterMethodsOrDie(env, "com/android/internal/os/ZygoteCommandBuffer", gMethods,
+                              NELEM(gMethods));
+}
+
+}  // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a89043d..d5f5d28 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1284,15 +1284,15 @@
         android:backgroundPermission="android.permission.RECORD_BACKGROUND_AUDIO"
         android:protectionLevel="dangerous|instant" />
 
-    <!-- Allows an application to record audio while in the background.
-         <p>Protection level: dangerous
-    -->
+    <!-- @SystemApi @TestApi Allows an application to record audio while in the background.
+         This permission is not intended to be held by apps.
+         <p>Protection level: internal
+        @hide -->
     <permission android:name="android.permission.RECORD_BACKGROUND_AUDIO"
         android:permissionGroup="android.permission-group.UNDEFINED"
         android:label="@string/permlab_recordBackgroundAudio"
         android:description="@string/permdesc_recordBackgroundAudio"
-        android:permissionFlags="hardRestricted|installerExemptIgnored"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="internal" />
 
     <!-- ====================================================================== -->
     <!-- Permissions for activity recognition                        -->
@@ -1368,15 +1368,15 @@
         android:backgroundPermission="android.permission.BACKGROUND_CAMERA"
         android:protectionLevel="dangerous|instant" />
 
-    <!-- Required to be able to access the camera device in the background.
-         <p>Protection level: dangerous
-    -->
+    <!-- @SystemApi @TestApi Required to be able to access the camera device in the background.
+         This permission is not intended to be held by apps.
+         <p>Protection level: internal
+        @hide -->
     <permission android:name="android.permission.BACKGROUND_CAMERA"
             android:permissionGroup="android.permission-group.UNDEFINED"
             android:label="@string/permlab_backgroundCamera"
             android:description="@string/permdesc_backgroundCamera"
-            android:permissionFlags="hardRestricted|installerExemptIgnored"
-            android:protectionLevel="dangerous" />
+            android:protectionLevel="internal" />
 
     <!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
            system only camera devices.
@@ -3982,17 +3982,6 @@
     <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to use the package installer v2 APIs.
-         <p>The package installer v2 APIs are still a work in progress and we're
-         currently validating they work in all scenarios.
-         <p>Not for use by third-party applications.
-         TODO(b/152310230): use this permission to protect only Incremental installations
-         once the APIs are confirmed to be sufficient.
-         @hide
-    -->
-    <permission android:name="com.android.permission.USE_INSTALLER_V2"
-        android:protectionLevel="signature|verifier" />
-
     <!-- Allows an application to use System Data Loaders.
          <p>Not for use by third-party applications.
          @hide
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index d79cb74..b83611bc 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -91,26 +91,11 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
-        android:layout_marginBottom="@dimen/notification_headerless_margin_minimum"
-        android:layout_marginTop="@dimen/notification_headerless_margin_minimum"
+        android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
+        android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
         android:orientation="vertical"
         >
 
-        <!--
-        This invisible FrameLayout is here as a collapsible padding.  Having a layout_weight=1 is
-        what causes this view (and it's counterpart at the opposite end) to collapse before the
-        actual content views do.
-        This pair of 10dp collapsible paddings (plus the 16dp fixed margins) allow us to support
-        headerless notifications of 1-3 lines (where each line is 20dp tall) where the 1-line
-        variant is 56dp and the 2- and 3-line variants are both 76dp.
-        -->
-        <FrameLayout
-            android:id="@+id/notification_headerless_margin_extra_top"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_headerless_margin_extra"
-            android:layout_weight="1"
-            />
-
         <!-- extends ViewGroup -->
         <NotificationTopLineView
             android:id="@+id/notification_top_line"
@@ -153,12 +138,20 @@
             android:orientation="vertical"
             >
 
-            <include
-                layout="@layout/notification_template_text"
+            <com.android.internal.widget.NotificationVanishingFrameLayout
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/notification_headerless_line_height"
-                android:layout_marginTop="0dp"
-                />
+                >
+                <!-- This is the simplest way to keep this text vertically centered without using
+                 gravity="center_vertical" which causes jumpiness in expansion animations. -->
+                <include
+                    layout="@layout/notification_template_text"
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_text_height"
+                    android:layout_gravity="center_vertical"
+                    android:layout_marginTop="0dp"
+                    />
+            </com.android.internal.widget.NotificationVanishingFrameLayout>
 
             <include
                 layout="@layout/notification_template_progress"
@@ -168,21 +161,6 @@
 
         </LinearLayout>
 
-        <!--
-        This invisible FrameLayout is here as a collapsible padding.  Having a layout_weight=1 is
-        what causes this view (and it's counterpart at the opposite end) to collapse before the
-        actual content views do.
-        This pair of 10dp collapsible paddings (plus the 16dp fixed margins) allow us to support
-        headerless notifications of 1-3 lines (where each line is 20dp tall) where the 1-line
-        variant is 56dp and the 2- and 3-line variants are both 76dp.
-        -->
-        <FrameLayout
-            android:id="@+id/notification_headerless_margin_extra_bottom"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_headerless_margin_extra"
-            android:layout_weight="1"
-            />
-
     </LinearLayout>
 
 </com.android.internal.widget.NotificationMaxHeightFrameLayout>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 29f2b6f..4410e94 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -26,6 +26,10 @@
 
     <color name="notification_default_color_dark">#ddffffff</color>
 
+    <color name="notification_primary_text_color_current">@color/notification_primary_text_color_dark</color>
+    <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_dark</color>
+    <color name="notification_default_color_current">@color/notification_default_color_dark</color>
+
     <color name="chooser_row_divider">@color/list_divider_color_dark</color>
     <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index b1bcf72..3fd82b8 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -28,9 +28,4 @@
     </style>
 
     <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" />
-
-    <style name="TextAppearance.Material.Notification">
-        <item name="textColor">@color/notification_secondary_text_color_dark</item>
-        <item name="textSize">@dimen/notification_text_size</item>
-    </style>
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 59c260c..d79c01f 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -143,6 +143,10 @@
     <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
     <color name="notification_default_color_light">#a3202124</color>
 
+    <color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color>
+    <color name="notification_secondary_text_color_current">@color/notification_secondary_text_color_light</color>
+    <color name="notification_default_color_current">@color/notification_default_color_light</color>
+
     <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
 
     <color name="notification_action_button_text_color">@color/notification_default_color</color>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 695a831..c2b6b99 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -319,26 +319,22 @@
     <!-- The top padding for the notification expand button. -->
     <dimen name="notification_expand_button_padding_top">1dp</dimen>
 
-    <!-- minimum vertical margin for the headerless notification content, when cap = 60dp -->
-    <dimen name="notification_headerless_margin_minimum">8dp</dimen>
+    <!-- Vertical margin for the headerless notification content, when content has 1 line -->
+    <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
+    <dimen name="notification_headerless_margin_oneline">16dp</dimen>
 
-    <!-- extra vertical margin for the headerless notification content, when cap = 60dp -->
-    <dimen name="notification_headerless_margin_extra">10dp</dimen>
-
-    <!-- minimum vertical margin for the headerless notification content, when cap = 48dp -->
-    <dimen name="notification_headerless_margin_constrained_minimum">14dp</dimen>
-
-    <!-- extra vertical margin for the headerless notification content, when cap = 48dp -->
-    <dimen name="notification_headerless_margin_constrained_extra">4dp</dimen>
+    <!-- Vertical margin for the headerless notification content, when content has 2 lines -->
+    <!-- 20 * 2 (margins) + 24 * 2 (2 lines) = 88 (notification) -->
+    <dimen name="notification_headerless_margin_twoline">20dp</dimen>
 
     <!-- The height of each of the 1 or 2 lines in the headerless notification template -->
-    <dimen name="notification_headerless_line_height">20sp</dimen>
+    <dimen name="notification_headerless_line_height">24dp</dimen>
 
     <!-- vertical margin for the headerless notification content -->
     <dimen name="notification_headerless_min_height">56dp</dimen>
 
     <!-- Height of a small notification in the status bar -->
-    <dimen name="notification_min_height">76dp</dimen>
+    <dimen name="notification_min_height">88dp</dimen>
 
     <!-- The width of the big icons in notifications. -->
     <dimen name="notification_large_icon_width">64dp</dimen>
@@ -738,10 +734,11 @@
     <!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
     <dimen name="notification_media_image_max_width">280dp</dimen>
     <!-- The size of the right icon -->
-    <dimen name="notification_right_icon_size">52dp</dimen>
+    <dimen name="notification_right_icon_size">48dp</dimen>
     <!-- The top and bottom margin of the right icon in the normal notification states -->
-    <dimen name="notification_right_icon_headerless_margin">12dp</dimen>
+    <dimen name="notification_right_icon_headerless_margin">20dp</dimen>
     <!-- The top margin of the right icon in the "big" notification states -->
+    <!--  TODO(b/181048615): Move the large icon below the expander in big states  -->
     <dimen name="notification_right_icon_big_margin_top">16dp</dimen>
     <!-- The size of the left icon -->
     <dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 576c44d..9eb2f14 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4538,7 +4538,7 @@
     <string name="color_correction_feature_name">Color Correction</string>
 
     <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
-    <string name="reduce_bright_colors_feature_name">Reduce Brightness</string>
+    <string name="reduce_bright_colors_feature_name">Reduce brightness</string>
 
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 158289a..3c4a5d4 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -466,18 +466,20 @@
     </style>
 
     <style name="TextAppearance.Material.Notification">
-        <item name="textColor">@color/notification_secondary_text_color_light</item>
+        <item name="textColor">@color/notification_secondary_text_color_current</item>
         <item name="textSize">@dimen/notification_text_size</item>
     </style>
 
     <style name="TextAppearance.Material.Notification.Reply" />
 
     <style name="TextAppearance.Material.Notification.Title">
+        <item name="textColor">@color/notification_primary_text_color_current</item>
         <item name="fontFamily">sans-serif-medium</item>
         <item name="textSize">@dimen/notification_title_text_size</item>
     </style>
 
     <style name="TextAppearance.Material.Notification.BigTitle">
+        <item name="textColor">@color/notification_primary_text_color_current</item>
         <item name="fontFamily">sans-serif-medium</item>
         <item name="textSize">@dimen/notification_big_title_text_size</item>
     </style>
@@ -492,9 +494,8 @@
 
     <style name="TextAppearance.Material.Notification.Time" parent="TextAppearance.Material.Notification.Info" />
 
-    <style name="TextAppearance.Material.Notification.Emphasis">
-        <item name="textColor">#66000000</item>
-    </style>
+    <!-- unused; keep identical to parent -->
+    <style name="TextAppearance.Material.Notification.Emphasis"/>
 
     <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title">
         <item name="android:textSize">16sp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7ad05de..a4dec22 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2895,8 +2895,6 @@
   <java-symbol type="id" name="alternate_expand_target" />
   <java-symbol type="id" name="notification_header" />
   <java-symbol type="id" name="notification_top_line" />
-  <java-symbol type="id" name="notification_headerless_margin_extra_top" />
-  <java-symbol type="id" name="notification_headerless_margin_extra_bottom" />
   <java-symbol type="id" name="time_divider" />
   <java-symbol type="id" name="header_text_divider" />
   <java-symbol type="id" name="header_text_secondary_divider" />
@@ -2918,8 +2916,8 @@
   <java-symbol type="dimen" name="notification_header_icon_size" />
   <java-symbol type="dimen" name="notification_header_app_name_margin_start" />
   <java-symbol type="dimen" name="notification_header_separating_margin" />
-  <java-symbol type="dimen" name="notification_headerless_margin_constrained_minimum" />
-  <java-symbol type="dimen" name="notification_headerless_margin_constrained_extra" />
+  <java-symbol type="dimen" name="notification_headerless_margin_oneline" />
+  <java-symbol type="dimen" name="notification_headerless_margin_twoline" />
   <java-symbol type="string" name="default_notification_channel_label" />
   <java-symbol type="string" name="importance_from_user" />
   <java-symbol type="string" name="importance_from_person" />
diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
index 1573c19..7cb6804 100644
--- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
+++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java
@@ -37,6 +37,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Parcel;
+import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -124,13 +125,13 @@
     }
 
     @Test
-    public void testUid() {
+    public void testUserHandle() {
         PeopleSpaceTile tile = new PeopleSpaceTile
                 .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps)
-                .setUid(42)
+                .setUserHandle(new UserHandle(0))
                 .build();
 
-        assertThat(tile.getUid()).isEqualTo(42);
+        assertThat(tile.getUserHandle()).isEqualTo(new UserHandle(0));
     }
 
     @Test
@@ -227,7 +228,7 @@
                 .setUserName("name")
                 .setUserIcon(mIcon)
                 .setContactUri(Uri.parse("contact"))
-                .setUid(42)
+                .setUserHandle(new UserHandle(1))
                 .setPackageName("package.name")
                 .setLastInteractionTimestamp(7L)
                 .setIsImportantConversation(true)
@@ -246,7 +247,7 @@
         assertThat(readTile.getUserName()).isEqualTo(tile.getUserName());
         assertThat(readTile.getUserIcon().toString()).isEqualTo(tile.getUserIcon().toString());
         assertThat(readTile.getContactUri()).isEqualTo(tile.getContactUri());
-        assertThat(readTile.getUid()).isEqualTo(tile.getUid());
+        assertThat(readTile.getUserHandle()).isEqualTo(tile.getUserHandle());
         assertThat(readTile.getPackageName()).isEqualTo(tile.getPackageName());
         assertThat(readTile.getLastInteractionTimestamp()).isEqualTo(
                 tile.getLastInteractionTimestamp());
diff --git a/core/tests/coretests/src/android/view/BlurAggregatorTest.java b/core/tests/coretests/src/android/view/BlurAggregatorTest.java
new file mode 100644
index 0000000..b01f275
--- /dev/null
+++ b/core/tests/coretests/src/android/view/BlurAggregatorTest.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable.Aggregator;
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable.BlurRegion;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BlurAggregatorTest {
+    private static final int TEST_BLUR_RADIUS = 30;
+    private static final int TEST_FRAME_NUMBER = 1;
+
+    private Context mContext;
+
+    private Aggregator mAggregator;
+    private BackgroundBlurDrawable mDrawable;
+
+    private ViewRootImpl mViewRoot;
+
+    @Before
+    public void setUp() {
+        mContext = getInstrumentation().getTargetContext();
+        getInstrumentation().runOnMainSync(() -> {
+            mViewRoot = new ViewRootImpl(mContext, mContext.getDisplayNoVerify());
+        });
+        mAggregator = new Aggregator(mViewRoot);
+        mDrawable = createTestBackgroundBlurDrawable();
+    }
+
+    private BackgroundBlurDrawable createTestBackgroundBlurDrawable() {
+        final BackgroundBlurDrawable drawable = mAggregator.createBackgroundBlurDrawable(mContext);
+        drawable.setBlurRadius(TEST_BLUR_RADIUS);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        return drawable;
+    }
+
+    @Test
+    public void testBlurRadiusUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setBlurRadius(0);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setBlurRadius(TEST_BLUR_RADIUS);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
+        assertFalse(mAggregator.hasUpdates());
+
+    }
+
+    @Test
+    public void testAlphaUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setAlpha(20);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setAlpha(20);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setAlpha(0);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testCornerRadiusUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setCornerRadius(1f, 2f, 3f, 4f);
+        assertTrue(mAggregator.hasUpdates());
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testVisibleUpdatePropagatesToRenderThreadIfNeeded() {
+        mDrawable.setVisible(false, /* restart= */false);
+        assertTrue(mAggregator.hasUpdates());
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(0, blurRegions.length);
+        assertFalse(mAggregator.hasUpdates());
+
+        mDrawable.setVisible(true, /* restart= */ false);
+        assertTrue(mAggregator.hasUpdates());
+        blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(TEST_BLUR_RADIUS, blurRegions[0].blurRadius);
+        assertFalse(mAggregator.hasUpdates());
+    }
+
+    @Test
+    public void testBlurRegionCopyForRtIsSameIfNoUiUpdates() {
+        mDrawable.setBlurRadius(30);
+        BlurRegion[] blurRegions1 = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions1.length);
+        assertEquals(30, blurRegions1[0].blurRadius);
+
+        BlurRegion[] blurRegions2 = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(blurRegions1, blurRegions2);
+    }
+
+    @Test
+    public void testPositionUpdateAppearsInBlurRegion() {
+        BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        mAggregator.getBlurRegionsToDispatchToSf(TEST_FRAME_NUMBER, blurRegions,
+                mAggregator.hasUpdates());
+        assertEquals(1, blurRegions[0].rect.left);
+        assertEquals(2, blurRegions[0].rect.top);
+        assertEquals(3, blurRegions[0].rect.right);
+        assertEquals(4, blurRegions[0].rect.bottom);
+    }
+
+    @Test
+    public void testNoBlurRegionsDispatchedWhenNoUpdates() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNull(blurRegionsForSf);
+    }
+
+    @Test
+    public void testBlurRegionDispatchedIfOnlyDrawableUpdated() {
+        mDrawable.setBlurRadius(50);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertTrue(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals(50f, blurRegionsForSf[0][0]);
+    }
+
+    @Test
+    public void testBlurRegionDispatchedIfOnlyPositionUpdated() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+    }
+
+    @Test
+    public void testPositionUpdateIsAppliedInNextFrameIfMissed() {
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertFalse(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+    }
+
+    @Test
+    public void testMultipleDrawablesDispatchedToSfIfOneIsUpdated() {
+        final BackgroundBlurDrawable drawable2 = createTestBackgroundBlurDrawable();
+        drawable2.setBlurRadius(50);
+        final boolean hasUpdates = mAggregator.hasUpdates();
+        assertTrue(hasUpdates);
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(2, blurRegions.length);
+
+        // Check that an update in one of the drawables triggers a dispatch of all blur regions
+        float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(2, blurRegionsForSf.length);
+
+        // Check that the Aggregator deleted all position updates for frame TEST_FRAME_NUMBER
+        blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
+        assertNull(blurRegionsForSf);
+
+        // Check that a position update triggers a dispatch of all blur regions
+        drawable2.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, hasUpdates);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(2, blurRegionsForSf.length);
+    }
+
+    @Test
+    public void testUiThreadUpdatesDoNotChangeStateOnRenderThread() {
+        // Updates for frame N
+        mDrawable.setBlurRadius(50);
+        mDrawable.setCornerRadius(1, 2, 3, 4);
+        mDrawable.setAlpha(20);
+
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+        assertEquals(1, blurRegions.length);
+        assertEquals(50, blurRegions[0].blurRadius);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+
+        // Updates for frame N+1
+        mDrawable.setBlurRadius(60);
+        mDrawable.setCornerRadius(10, 20, 30, 40);
+        mDrawable.setAlpha(40);
+
+        // Assert state for frame N is untouched
+        assertEquals(50, blurRegions[0].blurRadius);
+        assertEquals(20 / 255f, blurRegions[0].alpha);
+        assertEquals(1f, blurRegions[0].cornerRadiusTL);
+        assertEquals(2f, blurRegions[0].cornerRadiusTR);
+        assertEquals(3f, blurRegions[0].cornerRadiusBL);
+        assertEquals(4f, blurRegions[0].cornerRadiusBR);
+    }
+
+    @Test
+    public void testPositionUpdatesForFutureFramesAreNotAppliedForCurrentFrame() {
+        final BlurRegion[] blurRegions = mAggregator.getBlurRegionsCopyForRT();
+
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER, 1, 2, 3, 4);
+        mDrawable.mPositionUpdateListener.positionChanged(TEST_FRAME_NUMBER + 1, 5, 6, 7, 8);
+
+        final float[][] blurRegionsForSf = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER, blurRegions, /* hasUiUpdates= */ false);
+        assertNotNull(blurRegionsForSf);
+        assertEquals(1, blurRegionsForSf.length);
+        // Assert state for first frame is not affected by update for second frame
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSf[0][0]);
+        assertEquals(1f, blurRegionsForSf[0][2]);
+        assertEquals(2f, blurRegionsForSf[0][3]);
+        assertEquals(3f, blurRegionsForSf[0][4]);
+        assertEquals(4f, blurRegionsForSf[0][5]);
+
+        final float[][] blurRegionsForSfForNextFrame = mAggregator.getBlurRegionsToDispatchToSf(
+                TEST_FRAME_NUMBER + 1, blurRegions, /* hasUiUpdates= */ false);
+        assertNotNull(blurRegionsForSfForNextFrame);
+        assertEquals(1, blurRegionsForSfForNextFrame.length);
+        // Assert second frame updates are applied normally
+        assertEquals((float) TEST_BLUR_RADIUS, blurRegionsForSfForNextFrame[0][0]);
+        assertEquals(5f, blurRegionsForSfForNextFrame[0][2]);
+        assertEquals(6f, blurRegionsForSfForNextFrame[0][3]);
+        assertEquals(7f, blurRegionsForSfForNextFrame[0][4]);
+        assertEquals(8f, blurRegionsForSfForNextFrame[0][5]);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index b9cf1e4..516fb76 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -16,16 +16,12 @@
 
 package android.view;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,6 +31,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.ICancellationSignal;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -42,9 +39,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
 
 /**
  * Tests of {@link ScrollCaptureConnection}.
@@ -56,261 +51,138 @@
     private final Point mPositionInWindow = new Point(1, 2);
     private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5);
     private final Rect mScrollBounds = new Rect(3, 4, 5, 6);
+    private final TestScrollCaptureCallback mCallback = new TestScrollCaptureCallback();
+
+    private ScrollCaptureTarget mTarget;
+    private ScrollCaptureConnection mConnection;
 
     private Handler mHandler;
-    private ScrollCaptureTarget mTarget1;
 
     @Mock
     private Surface mSurface;
     @Mock
-    private IScrollCaptureCallbacks mConnectionCallbacks;
+    private IScrollCaptureCallbacks mRemote;
     @Mock
-    private View mMockView1;
-    @Mock
-    private ScrollCaptureCallback mCallback1;
+    private View mView;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mHandler = new Handler(getTargetContext().getMainLooper());
+        when(mSurface.isValid()).thenReturn(true);
+        when(mView.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE);
 
-        when(mMockView1.getHandler()).thenReturn(mHandler);
-        when(mMockView1.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        mTarget1 = new ScrollCaptureTarget(
-                mMockView1, mLocalVisibleRect, mPositionInWindow, mCallback1);
-        mTarget1.setScrollBounds(mScrollBounds);
-    }
-
-    /** Test the DelayedAction timeout helper class works as expected. */
-    @Test
-    public void testDelayedAction() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(200);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        getInstrumentation().waitForIdleSync();
-        assertFalse(delayed.cancel());
-        assertFalse(delayed.timeoutNow());
-        verify(action, times(1)).run();
-    }
-
-    /** Test the DelayedAction cancel() */
-    @Test
-    public void testDelayedAction_cancel() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(50);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        assertTrue(delayed.cancel());
-        assertFalse(delayed.timeoutNow());
-        try {
-            Thread.sleep(200);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        getInstrumentation().waitForIdleSync();
-        verify(action, never()).run();
-    }
-
-    /** Test the DelayedAction timeoutNow() - for testing only */
-    @Test
-    public void testDelayedAction_timeoutNow() {
-        Runnable action = Mockito.mock(Runnable.class);
-        ScrollCaptureConnection.DelayedAction delayed =
-                new ScrollCaptureConnection.DelayedAction(mHandler, 100, action);
-        try {
-            Thread.sleep(50);
-        } catch (InterruptedException ex) {
-            /* ignore */
-        }
-        assertTrue(delayed.timeoutNow());
-        assertFalse(delayed.cancel());
-        getInstrumentation().waitForIdleSync();
-        verify(action, times(1)).run();
+        mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback);
+        mTarget.setScrollBounds(mScrollBounds);
+        mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
     }
 
     /** Test creating a client with valid info */
     @Test
     public void testConstruction() {
-        new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
+        ScrollCaptureTarget target = new ScrollCaptureTarget(
+                mView, mLocalVisibleRect, mPositionInWindow, mCallback);
+        target.setScrollBounds(new Rect(1, 2, 3, 4));
+        new ScrollCaptureConnection(Runnable::run, target, mRemote);
     }
 
     /** Test creating a client fails if arguments are not valid. */
     @Test
     public void testConstruction_requiresScrollBounds() {
         try {
-            mTarget1.setScrollBounds(null);
-            new ScrollCaptureConnection(mTarget1, mConnectionCallbacks);
+            mTarget.setScrollBounds(null);
+            new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
             fail("An exception was expected.");
         } catch (RuntimeException ex) {
             // Ignore, expected.
         }
     }
 
-    @SuppressWarnings("SameParameterValue")
-    private static Answer<Void> runRunnable(int arg) {
-        return invocation -> {
-            Runnable r = invocation.getArgument(arg);
-            r.run();
-            return null;
-        };
-    }
-
-    @SuppressWarnings("SameParameterValue")
-    private static Answer<Void> reportBufferSent(int sessionArg, long frameNum, Rect capturedArea) {
-        return invocation -> {
-            ScrollCaptureSession session = invocation.getArgument(sessionArg);
-            session.notifyBufferSent(frameNum, capturedArea);
-            return null;
-        };
-    }
-
     /** @see ScrollCaptureConnection#startCapture(Surface) */
     @Test
     public void testStartCapture() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
+        mConnection.startCapture(mSurface);
 
-        // Have the session start accepted immediately
-        doAnswer(runRunnable(1)).when(mCallback1)
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        connection.startCapture(mSurface);
-        getInstrumentation().waitForIdleSync();
+        mCallback.completeStartRequest();
+        assertTrue(mConnection.isStarted());
 
-        verify(mCallback1, times(1))
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        verify(mConnectionCallbacks, times(1)).onCaptureStarted();
-        verifyNoMoreInteractions(mConnectionCallbacks);
+        verify(mRemote, times(1)).onCaptureStarted();
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testStartCaptureTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        connection.startCapture(mSurface);
+    public void testStartCapture_cancellation() throws Exception {
+        ICancellationSignal signal = mConnection.startCapture(mSurface);
+        signal.cancel();
 
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
+        mCallback.completeStartRequest();
+        assertFalse(mConnection.isStarted());
 
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-    }
-
-    private void startCapture(ScrollCaptureConnection connection) throws Exception {
-        doAnswer(runRunnable(1)).when(mCallback1)
-                .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class));
-        connection.startCapture(mSurface);
-        getInstrumentation().waitForIdleSync();
-        reset(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     /** @see ScrollCaptureConnection#requestImage(Rect) */
     @Test
     public void testRequestImage() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Stub the callback to complete the request immediately
-        doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4)))
-                .when(mCallback1)
-                .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class));
+        mConnection.requestImage(new Rect(1, 2, 3, 4));
+        mCallback.completeImageRequest(new Rect(1, 2, 3, 4));
 
-        // Make the inbound binder call
-        connection.requestImage(new Rect(1, 2, 3, 4));
-
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureImageRequest(
-                any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4)));
-
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1))
-                .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4)));
-
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verify(mRemote, times(1))
+                .onImageRequestCompleted(eq(0), eq(new Rect(1, 2, 3, 4)));
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testRequestImageTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+    public void testRequestImage_cancellation() throws Exception {
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Make the inbound binder call
-        connection.requestImage(new Rect(1, 2, 3, 4));
+        ICancellationSignal signal = mConnection.requestImage(new Rect(1, 2, 3, 4));
+        signal.cancel();
+        mCallback.completeImageRequest(new Rect(1, 2, 3, 4));
 
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureImageRequest(
-                any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4)));
-
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
-        getInstrumentation().waitForIdleSync();
-
-        // (callback not stubbed, does nothing)
-        // Timeout triggers request to end capture
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     /** @see ScrollCaptureConnection#endCapture() */
     @Test
     public void testEndCapture() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Stub the callback to complete the request immediately
-        doAnswer(runRunnable(0))
-                .when(mCallback1)
-                .onScrollCaptureEnd(any(Runnable.class));
+        mConnection.endCapture();
+        mCallback.completeEndRequest();
 
-        // Make the inbound binder call
-        connection.endCapture();
+        // And the reply is sent
+        verify(mRemote, times(1)).onCaptureEnded();
+        verifyNoMoreInteractions(mRemote);
+    }
 
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
+    /** @see ScrollCaptureConnection#endCapture() */
+    @Test
+    public void testEndCapture_cancellation() throws Exception {
+        mConnection.startCapture(mSurface);
+        mCallback.completeStartRequest();
+        reset(mRemote);
 
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
+        ICancellationSignal signal = mConnection.endCapture();
+        signal.cancel();
+        mCallback.completeEndRequest();
 
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+        verifyNoMoreInteractions(mRemote);
     }
 
     @Test
-    public void testEndCaptureTimeout() throws Exception {
-        final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1,
-                mConnectionCallbacks);
-        startCapture(connection);
-
-        // Make the inbound binder call
-        connection.endCapture();
-
-        // Wait for handler thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class));
-
-        // Force timeout to fire
-        connection.getTimeoutAction().timeoutNow();
-
-        // Wait for binder thread dispatch
-        getInstrumentation().waitForIdleSync();
-        verify(mConnectionCallbacks, times(1)).onConnectionClosed();
-
-        verifyNoMoreInteractions(mCallback1, mConnectionCallbacks);
+    public void testClose() throws Exception {
+        mConnection.close();
+        assertFalse(mConnection.isConnected());
+        verifyNoMoreInteractions(mRemote);
     }
+
 }
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
new file mode 100644
index 0000000..cc229e1
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ScrollCaptureSearchResultsTest.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static androidx.test.InstrumentationRegistry.getTargetContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Tests of {@link ScrollCaptureTargetSelector}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ScrollCaptureSearchResultsTest {
+
+
+    private static final Rect EMPTY_RECT = new Rect();
+    private static final String TAG = "Test";
+
+    private final Executor mDirectExec = Runnable::run;
+    private Executor mBgExec;
+
+    @Before
+    public void setUp() {
+        mBgExec = Executors.newSingleThreadExecutor();
+    }
+
+    @Test
+    public void testNoTargets() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        assertTrue(results.isComplete());
+
+        assertNull("Expected null due to empty queue", results.getTopResult());
+    }
+
+    @Test
+    public void testNoValidTargets() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec);
+        callback1.setScrollBounds(EMPTY_RECT);
+        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
+                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // Supplies scrollBounds = empty rect
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec);
+        callback2.setScrollBounds(EMPTY_RECT);
+        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
+                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
+
+        results.addTarget(target1);
+        results.addTarget(target2);
+
+        assertTrue(results.isComplete());
+        assertNull("Expected null due to no valid targets", results.getTopResult());
+    }
+
+    @Test
+    public void testSingleTarget() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback(mDirectExec);
+        ScrollCaptureTarget target = createTarget(callback,
+                new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        callback.setScrollBounds(new Rect(2, 2, 18, 18));
+
+        results.addTarget(target);
+        assertTrue(results.isComplete());
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Excepted the same target as a result", target, result);
+        assertEquals("result has wrong scroll bounds",
+                new Rect(2, 2, 18, 18), result.getScrollBounds());
+    }
+
+    @Test
+    public void testSingleTarget_backgroundThread() throws InterruptedException {
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec);
+        ScrollCaptureTarget target1 = createTarget(callback1,
+                new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        callback1.setDelay(100);
+        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+
+        CountDownLatch latch = new CountDownLatch(1);
+        results.setOnCompleteListener(latch::countDown);
+        if (!latch.await(200, TimeUnit.MILLISECONDS)) {
+            fail("onComplete listener was expected");
+        }
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Excepted the single target1 as a result", target1, result);
+        assertEquals("Result has wrong scroll bounds",
+                new Rect(2, 2, 18, 18), result.getScrollBounds());
+    }
+
+    @Test
+    public void testRanking() {
+
+        // 1 - Empty
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mDirectExec);
+        callback1.setScrollBounds(EMPTY_RECT);
+        ViewGroup targetView1 = new FakeView(getTargetContext(), 0, 0, 60, 60, 1);
+        ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 2 - 10x10 + HINT_INCLUDE
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mDirectExec);
+        callback2.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView2 = new FakeView(getTargetContext(), 0, 0, 60, 60, 2);
+        ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2,
+                 new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_INCLUDE);
+
+        // 3 - 20x20 + AUTO
+        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mDirectExec);
+        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
+        ViewGroup targetView3 = new FakeView(getTargetContext(), 0, 0, 60, 60, 3);
+        ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 4 - 30x30 + AUTO
+        FakeScrollCaptureCallback callback4 = new FakeScrollCaptureCallback(mDirectExec);
+        callback4.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView4 = new FakeView(getTargetContext(), 0, 0, 60, 60, 4);
+        ScrollCaptureTarget target4 = createTargetWithView(targetView4, callback4,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        // 5 - 10x10 + child of #4
+        FakeScrollCaptureCallback callback5 = new FakeScrollCaptureCallback(mDirectExec);
+        callback5.setScrollBounds(new Rect(0, 0, 10, 10));
+        ViewGroup targetView5 = new FakeView(getTargetContext(), 0, 0, 60, 60, 5);
+        ScrollCaptureTarget target5 = createTargetWithView(targetView5, callback5,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+        targetView4.addView(targetView5);
+
+        // 6 - 20x20 + child of #4
+        FakeScrollCaptureCallback callback6 = new FakeScrollCaptureCallback(mDirectExec);
+        callback6.setScrollBounds(new Rect(0, 0, 20, 20));
+        ViewGroup targetView6 = new FakeView(getTargetContext(), 0, 0, 60, 60, 6);
+        ScrollCaptureTarget target6 = createTargetWithView(targetView6, callback6,
+                new Rect(0, 0, 60, 60), new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
+        targetView4.addView(targetView6);
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+        results.addTarget(target2);
+        results.addTarget(target3);
+        results.addTarget(target4);
+        results.addTarget(target5);
+        results.addTarget(target6);
+        assertTrue(results.isComplete());
+
+        // Verify "top" result
+        assertEquals(target2, results.getTopResult());
+
+        // Verify priority ("best" first)
+        assertThat(results.getTargets())
+                .containsExactly(
+                        target2,
+                        target6,
+                        target5,
+                        target4,
+                        target3,
+                        target1);
+    }
+
+    /**
+     * If a timeout expires, late results are ignored.
+     */
+    @Test
+    public void testTimeout() {
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+
+        // callback 1, 10x10, hint=AUTO, responds after 100ms from bg thread
+        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback(mBgExec);
+        callback1.setScrollBounds(new Rect(5, 5, 15, 15));
+        callback1.setDelay(100);
+        ScrollCaptureTarget target1 = createTarget(
+                callback1, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        results.addTarget(target1);
+
+        // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread
+        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback(mBgExec);
+        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
+        callback2.setDelay(1000);
+        ScrollCaptureTarget target2 = createTarget(
+                callback2, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_AUTO);
+        results.addTarget(target2);
+
+        // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread
+        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback(mBgExec);
+        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
+        callback3.setDelay(1500);
+        ScrollCaptureTarget target3 = createTarget(
+                callback3, new Rect(20, 30, 40, 50), new Point(10, 10),
+                View.SCROLL_CAPTURE_HINT_INCLUDE);
+        results.addTarget(target3);
+
+        // callback 1 will be received
+        // callback 2 & 3 will be ignored due to timeout
+        SystemClock.sleep(500);
+        results.finish();
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Expected target1 as the result, due to timeouts of others", target1, result);
+        assertEquals("callback1 should have been called",
+                1, callback1.getOnScrollCaptureSearchCount());
+        assertEquals("callback2 should have been called",
+                1, callback2.getOnScrollCaptureSearchCount());
+        assertEquals("callback3 should have been called",
+                1, callback3.getOnScrollCaptureSearchCount());
+
+        assertEquals("result has wrong scroll bounds",
+                new Rect(5, 5, 15, 15), result.getScrollBounds());
+        assertNull("target2 should not have been updated",
+                target2.getScrollBounds());
+        assertNull("target3 should not have been updated",
+                target3.getScrollBounds());
+    }
+
+    @Test
+    public void testWithCallbackMultipleReplies() {
+        // Calls response methods 3 times each
+        ScrollCaptureCallback callback1 = new CallbackStub() {
+            @Override
+            public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                    @NonNull Consumer<Rect> onReady) {
+                onReady.accept(new Rect(1, 2, 3, 4));
+                onReady.accept(new Rect(9, 10, 11, 12));
+            }
+        };
+
+        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
+                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
+
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(mDirectExec);
+        results.addTarget(target1);
+        assertTrue(results.isComplete());
+
+        ScrollCaptureTarget result = results.getTopResult();
+        assertSame("Expected target1", target1, result);
+        assertEquals("result has wrong scroll bounds",
+                new Rect(1, 2, 3, 4), result.getScrollBounds());
+    }
+
+    private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) {
+        view.setScrollCaptureHint(scrollCaptureHint);
+        view.onVisibilityAggregated(true);
+        // Treat any offset as padding, outset localVisibleRect on all sides and use this as
+        // child bounds
+        Rect bounds = new Rect(localVisibleRect);
+        bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top);
+        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        view.onVisibilityAggregated(true);
+    }
+
+    private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect,
+            Point positionInWindow, int scrollCaptureHint) {
+        View mockView = new View(getTargetContext());
+        return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow,
+                scrollCaptureHint);
+    }
+
+    private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback,
+            Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) {
+        setupTargetView(view, localVisibleRect, scrollCaptureHint);
+        return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback);
+    }
+
+
+    static class FakeView extends ViewGroup implements ViewParent {
+        FakeView(Context context, int l, int t, int r, int b, int id) {
+            super(context);
+            layout(l, t, r, b);
+            setId(id);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        }
+    }
+
+    static class FakeScrollCaptureCallback implements ScrollCaptureCallback {
+        private final Executor mExecutor;
+        private Rect mScrollBounds;
+        private long mDelayMillis;
+        private int mOnScrollCaptureSearchCount;
+        FakeScrollCaptureCallback(Executor executor) {
+            mExecutor = executor;
+        }
+        public int getOnScrollCaptureSearchCount() {
+            return mOnScrollCaptureSearchCount;
+        }
+
+        @Override
+        public void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) {
+            mOnScrollCaptureSearchCount++;
+            run(() -> {
+                Rect b = getScrollBounds();
+                onReady.accept(b);
+            });
+        }
+
+        @Override
+        public void onScrollCaptureStart(ScrollCaptureSession session, CancellationSignal signal,
+                Runnable onReady) {
+            run(onReady);
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(ScrollCaptureSession session,
+                CancellationSignal signal, Rect captureArea, Consumer<Rect> onReady) {
+            run(() -> onReady.accept(captureArea));
+        }
+
+        @Override
+        public void onScrollCaptureEnd(Runnable onReady) {
+            run(onReady);
+        }
+
+        public void setScrollBounds(@Nullable Rect scrollBounds) {
+            mScrollBounds = scrollBounds;
+        }
+
+        public void setDelay(long delayMillis) {
+            mDelayMillis = delayMillis;
+        }
+
+        protected Rect getScrollBounds() {
+            return mScrollBounds;
+        }
+
+        protected void run(Runnable r) {
+            mExecutor.execute(() -> {
+                delay();
+                r.run();
+            });
+        }
+
+        protected void delay() {
+            if (mDelayMillis > 0) {
+                try {
+                    Thread.sleep(mDelayMillis);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+    static class CallbackStub implements ScrollCaptureCallback {
+        @Override
+        public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                @NonNull Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+                Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java b/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java
deleted file mode 100644
index 8b21b8e..0000000
--- a/core/tests/coretests/src/android/view/ScrollCaptureTargetResolverTest.java
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.LinkedList;
-import java.util.function.Consumer;
-
-/**
- * Tests of {@link ScrollCaptureTargetResolver}.
- */
-@RunWith(AndroidJUnit4.class)
-public class ScrollCaptureTargetResolverTest {
-
-    private static final long TEST_TIMEOUT_MS = 2000;
-    private static final long RESOLVER_TIMEOUT_MS = 1000;
-
-    private Handler mHandler;
-    private TargetConsumer mTargetConsumer;
-
-    @Before
-    public void setUp() {
-        mTargetConsumer = new TargetConsumer();
-        mHandler = new Handler(getTargetContext().getMainLooper());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testEmptyQueue() throws InterruptedException {
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(new LinkedList<>());
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertNull("Expected null due to empty queue", result);
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testNoValidTargets() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        // Supplies scrollBounds = null
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(null);
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Supplies scrollBounds = empty rect
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect());
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertNull("Expected null due to no valid targets", result);
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testSingleTarget() throws InterruptedException {
-        FakeScrollCaptureCallback callback = new FakeScrollCaptureCallback();
-        ScrollCaptureTarget target = createTarget(callback,
-                new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        callback.setScrollBounds(new Rect(2, 2, 18, 18));
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target);
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Excepted the same target as a result", target, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(2, 2, 18, 18), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testSingleTarget_backgroundThread() throws InterruptedException {
-        BackgroundTestCallback callback1 = new BackgroundTestCallback();
-        ScrollCaptureTarget target1 = createTarget(callback1,
-                new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        callback1.setDelay(100);
-        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target1);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Excepted the single target1 as a result", target1, result);
-        assertEquals("Result has wrong scroll bounds",
-                new Rect(2, 2, 18, 18), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testPreferNonEmptyBounds() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect());
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(null);
-        ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50),
-                new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2); // scrollBounds not null or empty()
-        targetQueue.add(target3);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertEquals("Expected " + target2 + " as a result", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 20, 20), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testPreferHintInclude() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(1, 1, 19, 19));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(0, 20), View.SCROLL_CAPTURE_HINT_INCLUDE);
-
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(new Rect(2, 2, 18, 18));
-        ScrollCaptureTarget target3 = createTarget(callback3, new Rect(20, 30, 40, 50),
-                new Point(0, 40), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1);
-        targetQueue.add(target2); // * INCLUDE > AUTO
-        targetQueue.add(target3);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertEquals("input = " + targetQueue + " Expected " + target2
-                + " as the result, due to hint=INCLUDE", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(1, 1, 19, 19), result.getScrollBounds());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testDescendantPreferred() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        ViewGroup targetView1 = new FakeRootView(getTargetContext(), 0, 0, 60, 60); // 60x60
-        ViewGroup targetView2 = new FakeRootView(getTargetContext(), 20, 30, 40, 50); // 20x20
-        ViewGroup targetView3 = new FakeRootView(getTargetContext(), 5, 5, 15, 15); // 10x10
-
-        targetView1.addView(targetView2);
-        targetView2.addView(targetView3);
-
-        // Create first target with an unrelated parent
-        FakeScrollCaptureCallback callback1 = new FakeScrollCaptureCallback();
-        callback1.setScrollBounds(new Rect(0, 0, 60, 60));
-        ScrollCaptureTarget target1 = createTargetWithView(targetView1, callback1,
-                new Rect(0, 0, 60, 60),
-                new Point(0, 0), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Create second target associated with a view within parent2
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTargetWithView(targetView2, callback2,
-                new Rect(0, 0, 20, 20),
-                new Point(20, 30), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        // Create third target associated with a view within parent3
-        FakeScrollCaptureCallback callback3 = new FakeScrollCaptureCallback();
-        callback3.setScrollBounds(new Rect(0, 0, 15, 15));
-        ScrollCaptureTarget target3 = createTargetWithView(targetView3, callback3,
-                new Rect(0, 0, 15, 15),
-                new Point(25, 35), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        targetQueue.add(target1); // auto, 60x60
-        targetQueue.add(target2); // auto, 20x20
-        targetQueue.add(target3); // auto, 15x15 <- innermost scrollable
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        // Test only
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target3 as the result, due to relation", target3, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 15, 15), result.getScrollBounds());
-    }
-
-    /**
-     * If a timeout expires, late results are ignored.
-     */
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testTimeout() throws InterruptedException {
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-
-        // callback 1, 10x10, hint=AUTO, responds immediately from bg thread
-        BackgroundTestCallback callback1 = new BackgroundTestCallback();
-        callback1.setScrollBounds(new Rect(5, 5, 15, 15));
-        ScrollCaptureTarget target1 = createTarget(
-                callback1, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        targetQueue.add(target1);
-
-        // callback 2, 20x20, hint=AUTO, responds after 5s from bg thread
-        BackgroundTestCallback callback2 = new BackgroundTestCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        callback2.setDelay(5000);
-        ScrollCaptureTarget target2 = createTarget(
-                callback2, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_AUTO);
-        targetQueue.add(target2);
-
-        // callback 3, 20x20, hint=INCLUDE, responds after 10s from bg thread
-        BackgroundTestCallback callback3 = new BackgroundTestCallback();
-        callback3.setScrollBounds(new Rect(0, 0, 20, 20));
-        callback3.setDelay(10000);
-        ScrollCaptureTarget target3 = createTarget(
-                callback3, new Rect(20, 30, 40, 50), new Point(10, 10),
-                View.SCROLL_CAPTURE_HINT_INCLUDE);
-        targetQueue.add(target3);
-
-        // callback 1 will be received
-        // callback 2 & 3 will be ignored due to timeout
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target1 as the result, due to timeouts of others", target1, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(5, 5, 15, 15), result.getScrollBounds());
-        assertEquals("callback1 should have been called",
-                1, callback1.getOnScrollCaptureSearchCount());
-        assertEquals("callback2 should have been called",
-                1, callback2.getOnScrollCaptureSearchCount());
-        assertEquals("callback3 should have been called",
-                1, callback3.getOnScrollCaptureSearchCount());
-    }
-
-    @Test(timeout = TEST_TIMEOUT_MS)
-    public void testWithCallbackMultipleReplies() throws InterruptedException {
-        // Calls response methods 3 times each
-        RepeatingCaptureCallback callback1 = new RepeatingCaptureCallback(3);
-        callback1.setScrollBounds(new Rect(2, 2, 18, 18));
-        ScrollCaptureTarget target1 = createTarget(callback1, new Rect(20, 30, 40, 50),
-                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        FakeScrollCaptureCallback callback2 = new FakeScrollCaptureCallback();
-        callback2.setScrollBounds(new Rect(0, 0, 20, 20));
-        ScrollCaptureTarget target2 = createTarget(callback2, new Rect(20, 30, 40, 50),
-                new Point(10, 10), View.SCROLL_CAPTURE_HINT_AUTO);
-
-        LinkedList<ScrollCaptureTarget> targetQueue = new LinkedList<>();
-        targetQueue.add(target1);
-        targetQueue.add(target2);
-
-        ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetQueue);
-        resolver.start(mHandler, RESOLVER_TIMEOUT_MS, mTargetConsumer);
-
-        resolver.waitForResult();
-
-        ScrollCaptureTarget result = mTargetConsumer.getLastValue();
-        assertSame("Expected target2 as the result, due to hint=INCLUDE", target2, result);
-        assertEquals("result has wrong scroll bounds",
-                new Rect(0, 0, 20, 20), result.getScrollBounds());
-        assertEquals("callback1 should have been called once",
-                1, callback1.getOnScrollCaptureSearchCount());
-        assertEquals("callback2 should have been called once",
-                1, callback2.getOnScrollCaptureSearchCount());
-    }
-
-    private static class TargetConsumer implements Consumer<ScrollCaptureTarget> {
-        volatile ScrollCaptureTarget mResult;
-        int mAcceptCount;
-
-        ScrollCaptureTarget getLastValue() {
-            return mResult;
-        }
-
-        int acceptCount() {
-            return mAcceptCount;
-        }
-
-        @Override
-        public void accept(@Nullable ScrollCaptureTarget t) {
-            mAcceptCount++;
-            mResult = t;
-        }
-    }
-
-    private void setupTargetView(View view, Rect localVisibleRect, int scrollCaptureHint) {
-        view.setScrollCaptureHint(scrollCaptureHint);
-        view.onVisibilityAggregated(true);
-        // Treat any offset as padding, outset localVisibleRect on all sides and use this as
-        // child bounds
-        Rect bounds = new Rect(localVisibleRect);
-        bounds.inset(-bounds.left, -bounds.top, bounds.left, bounds.top);
-        view.layout(bounds.left, bounds.top, bounds.right, bounds.bottom);
-        view.onVisibilityAggregated(true);
-    }
-
-    private ScrollCaptureTarget createTarget(ScrollCaptureCallback callback, Rect localVisibleRect,
-            Point positionInWindow, int scrollCaptureHint) {
-        View mockView = new View(getTargetContext());
-        return createTargetWithView(mockView, callback, localVisibleRect, positionInWindow,
-                scrollCaptureHint);
-    }
-
-    private ScrollCaptureTarget createTargetWithView(View view, ScrollCaptureCallback callback,
-            Rect localVisibleRect, Point positionInWindow, int scrollCaptureHint) {
-        setupTargetView(view, localVisibleRect, scrollCaptureHint);
-        return new ScrollCaptureTarget(view, localVisibleRect, positionInWindow, callback);
-    }
-
-
-    static class FakeRootView extends ViewGroup implements ViewParent {
-        FakeRootView(Context context, int l, int t, int r, int b) {
-            super(context);
-            layout(l, t, r, b);
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        }
-    }
-
-    static class FakeScrollCaptureCallback implements ScrollCaptureCallback {
-        private Rect mScrollBounds;
-        private long mDelayMillis;
-        private int mOnScrollCaptureSearchCount;
-
-        public int getOnScrollCaptureSearchCount() {
-            return mOnScrollCaptureSearchCount;
-        }
-
-        @Override
-        public void onScrollCaptureSearch(Consumer<Rect> onReady) {
-            mOnScrollCaptureSearchCount++;
-            run(() -> {
-                Rect b = getScrollBounds();
-                onReady.accept(b);
-            });
-        }
-
-        @Override
-        public void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) {
-            run(onReady);
-        }
-
-        @Override
-        public void onScrollCaptureImageRequest(ScrollCaptureSession session, Rect captureArea) {
-            run(() -> session.notifyBufferSent(0, captureArea));
-        }
-
-        @Override
-        public void onScrollCaptureEnd(Runnable onReady) {
-            run(onReady);
-        }
-
-        public void setScrollBounds(@Nullable Rect scrollBounds) {
-            mScrollBounds = scrollBounds;
-        }
-
-        public void setDelay(long delayMillis) {
-            mDelayMillis = delayMillis;
-        }
-
-        protected Rect getScrollBounds() {
-            return mScrollBounds;
-        }
-
-        protected void run(Runnable r) {
-            delay();
-            r.run();
-        }
-
-        protected void delay() {
-            if (mDelayMillis > 0) {
-                try {
-                    Thread.sleep(mDelayMillis);
-                } catch (InterruptedException e) {
-                    // Ignore
-                }
-            }
-        }
-    }
-
-    static class RepeatingCaptureCallback extends FakeScrollCaptureCallback {
-        private int mRepeatCount;
-
-        RepeatingCaptureCallback(int repeatCount) {
-            mRepeatCount = repeatCount;
-        }
-
-        protected void run(Runnable r) {
-            delay();
-            for (int i = 0; i < mRepeatCount; i++) {
-                r.run();
-            }
-        }
-    }
-
-    /** Response to async calls on an arbitrary background thread */
-    static class BackgroundTestCallback extends FakeScrollCaptureCallback {
-        static int sCount = 0;
-        private void runOnBackgroundThread(Runnable r) {
-            final Runnable target = () -> {
-                delay();
-                r.run();
-            };
-            Thread t = new Thread(target);
-            synchronized (BackgroundTestCallback.this) {
-                sCount++;
-            }
-            t.setName("Background-Thread-" + sCount);
-            t.start();
-        }
-
-        @Override
-        protected void run(Runnable r) {
-            runOnBackgroundThread(r);
-        }
-    }
-}
diff --git a/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java
new file mode 100644
index 0000000..1520c6e
--- /dev/null
+++ b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.*;
+
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+class TestScrollCaptureCallback implements ScrollCaptureCallback {
+    private Consumer<Rect> mSearchConsumer;
+    private Runnable mStartOnReady;
+    private Consumer<Rect> mImageOnComplete;
+    private Runnable mOnEndReady;
+    private volatile int mModCount;
+
+    @Override
+    public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+            @NonNull Consumer<Rect> onReady) {
+        mSearchConsumer = onReady;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        mStartOnReady = onReady;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+            @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+            @NonNull Consumer<Rect> onComplete) {
+        mImageOnComplete = onComplete;
+        mModCount++;
+    }
+
+    @Override
+    public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        mOnEndReady = onReady;
+    }
+
+    void completeSearchRequest(Rect scrollBounds) {
+        assertNotNull("Did not receive search request", mSearchConsumer);
+        mSearchConsumer.accept(scrollBounds);
+        mModCount++;
+    }
+
+    void verifyZeroInteractions() {
+        assertEquals("Expected zero interactions", 0, mModCount);
+    }
+
+    void completeStartRequest() {
+        assertNotNull("Did not receive start request", mStartOnReady);
+        mStartOnReady.run();
+    }
+
+    void completeImageRequest(Rect captured) {
+        assertNotNull("Did not receive image request", mImageOnComplete);
+        mImageOnComplete.accept(captured);
+    }
+
+    void completeEndRequest() {
+        assertNotNull("Did not receive end request", mOnEndReady);
+        mOnEndReady.run();
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
index 3af0533..41cd4c5 100644
--- a/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewGroupScrollCaptureTest.java
@@ -19,7 +19,9 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.testng.AssertJUnit.assertSame;
 
@@ -27,19 +29,20 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-import java.util.LinkedList;
-import java.util.Queue;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Exercises Scroll Capture search in {@link ViewGroup}.
@@ -50,10 +53,7 @@
 @RunWith(MockitoJUnitRunner.class)
 public class ViewGroupScrollCaptureTest {
 
-    @Mock
-    ScrollCaptureCallback mMockCallback;
-    @Mock
-    ScrollCaptureCallback mMockCallback2;
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
 
     /** Make sure the hint flags are saved and loaded correctly. */
     @Test
@@ -103,25 +103,24 @@
     public void testDispatchScrollCaptureSearch_noCallback_hintAuto() throws Exception {
         final Context context = getInstrumentation().getContext();
         final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200);
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
 
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Verify the system checked for fallback support
-        viewGroup.assertDispatchScrollCaptureCount(1);
-        viewGroup.assertLastDispatchScrollCaptureArgs(localVisibleRect, windowOffset);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.completeSearchRequest(new Rect(1, 2, 3, 4));
+        assertTrue(results.isComplete());
 
         // Verify the target is as expected.
-        assertEquals(1, targetList.size());
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("Target has the wrong callback", mMockCallback, target.getCallback());
+        ScrollCaptureTarget target = results.getTopResult();
+        assertNotNull("Target not found", target);
+        assertSame("Target has the wrong callback", callback, target.getCallback());
         assertSame("Target has the wrong View", viewGroup, target.getContainingView());
         assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
                 target.getContainingView().getScrollCaptureHint());
@@ -139,18 +138,22 @@
         final MockViewGroup viewGroup =
                 new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE);
 
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
+        assertTrue(results.isComplete());
 
         // Dispatch
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.verifyZeroInteractions();
 
         // Verify the results.
-        assertEquals("Target list size should be zero.", 0, targetList.size());
+        assertTrue("Results should be empty.", results.isEmpty());
     }
 
     /**
@@ -164,27 +167,34 @@
         final Context context = getInstrumentation().getContext();
         MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200);
 
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+        TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback();
+
         // With an already provided scroll capture callback
-        viewGroup.setScrollCaptureCallback(mMockCallback);
+        viewGroup.setScrollCaptureCallback(callback);
 
         // When system internal scroll capture is requested, this callback is returned.
-        viewGroup.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        viewGroup.setScrollCaptureCallbackInternalForTest(callback2);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Confirm that framework support was not requested,
-        // because this view already had a callback set.
-        viewGroup.assertCreateScrollCaptureCallbackInternalCount(0);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.completeSearchRequest(new Rect(1, 2, 3, 4));
 
         // Verify the target is as expected.
-        assertEquals(1, targetList.size());
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("Target has the wrong callback", mMockCallback, target.getCallback());
+        assertFalse(results.isEmpty());
+        assertTrue(results.isComplete());
+
+        // internal framework callback was not requested
+        callback2.verifyZeroInteractions();
+
+        ScrollCaptureTarget target = results.getTopResult();
+
+        assertNotNull("Target not found", target);
+        assertSame("Target has the wrong callback", callback, target.getCallback());
         assertSame("Target has the wrong View", viewGroup, target.getContainingView());
         assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
                 target.getContainingView().getScrollCaptureHint());
@@ -201,22 +211,22 @@
         final Context context = getInstrumentation().getContext();
         MockViewGroup viewGroup =
                 new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE);
+
+        TestScrollCaptureCallback callback = new TestScrollCaptureCallback();
+
         // With an already provided scroll capture callback
-        viewGroup.setScrollCaptureCallback(mMockCallback);
+        viewGroup.setScrollCaptureCallback(callback);
 
         Rect localVisibleRect = new Rect(0, 0, 200, 200);
         Point windowOffset = new Point();
-        LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup itself
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
-
-        // Confirm that framework support was not requested, because this view is excluded.
-        // (And because this view has a callback set.)
-        viewGroup.assertCreateScrollCaptureCallbackInternalCount(0);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback.verifyZeroInteractions();
 
         // Has callback, but hint=excluded, so excluded.
-        assertTrue(targetList.isEmpty());
+        assertNull(results.getTopResult());
     }
 
     /**
@@ -252,37 +262,43 @@
         // |               |          |
         // +---------------+----------+ (200,200)
 
-        // View 1 is clipped and not visible.
+        // View 1 is fully clipped and not visible.
         final MockView view1 = new MockView(context, 0, 0, 200, 25);
         viewGroup.addView(view1);
 
-        // View 2 is partially visible.
+        // View 2 is partially visible. (75x75)
         final MockView view2 = new MockView(context, 0, 25, 150, 100);
         viewGroup.addView(view2);
 
-        // View 3 is partially visible.
+        TestScrollCaptureCallback callback1 = new TestScrollCaptureCallback();
+
+        // View 3 is partially visible (175x50)
         // Pretend View3 can scroll by having framework provide fallback support
         final MockView view3 = new MockView(context, 0, 100, 200, 200);
         // When system internal scroll capture is requested for this view, return this callback.
-        view3.setScrollCaptureCallbackInternalForTest(mMockCallback);
+        view3.setScrollCaptureCallbackInternalForTest(callback1);
         viewGroup.addView(view3);
 
         // View 4 is invisible and should be ignored.
         final MockView view4 = new MockView(context, 150, 25, 200, 100, View.INVISIBLE);
         viewGroup.addView(view4);
 
-        // View 4 is invisible and should be ignored.
+        TestScrollCaptureCallback callback2 = new TestScrollCaptureCallback();
+
+        // View 5 is partially visible and explicitly included via flag. (25x50)
         final MockView view5 = new MockView(context, 150, 100, 200, 200);
-        // When system internal scroll capture is requested for this view, return this callback.
-        view5.setScrollCaptureCallback(mMockCallback2);
+        view5.setScrollCaptureCallback(callback2);
         view5.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE);
         viewGroup.addView(view5);
 
         // Where targets are added
-        final LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
+        final ScrollCaptureSearchResults results = new ScrollCaptureSearchResults(DIRECT_EXECUTOR);
 
         // Dispatch to the ViewGroup
-        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targetList);
+        viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results::addTarget);
+        callback1.completeSearchRequest(new Rect(0, 0, 200, 100));
+        callback2.completeSearchRequest(new Rect(0, 0, 50, 100));
+        assertTrue(results.isComplete());
 
         // View 1 is entirely clipped by the parent and not visible, dispatch
         // skips this view entirely.
@@ -317,18 +333,14 @@
         view5.assertCreateScrollCaptureCallbackInternalCount(0);
 
         // 2 views should have been returned, view3 & view5
-        assertEquals(2, targetList.size());
+        assertFalse(results.isEmpty());
+        assertTrue(results.isComplete());
 
-        ScrollCaptureTarget target = targetList.get(0);
-        assertSame("First target has the wrong View", view3, target.getContainingView());
-        assertSame("First target has the wrong callback", mMockCallback, target.getCallback());
-        assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
-                target.getContainingView().getScrollCaptureHint());
-
-        target = targetList.get(1);
-        assertSame("Second target has the wrong View", view5, target.getContainingView());
-        assertSame("Second target has the wrong callback", mMockCallback2, target.getCallback());
-        assertEquals("Second target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE,
+        ScrollCaptureTarget target = results.getTopResult();
+        assertNotNull("Target not found", target);
+        assertSame("Result is the wrong View", view5, target.getContainingView());
+        assertSame("Result is the wrong callback", callback2, target.getCallback());
+        assertEquals("First target hint is incorrect", View.SCROLL_CAPTURE_HINT_INCLUDE,
                 target.getContainingView().getScrollCaptureHint());
     }
 
@@ -371,7 +383,7 @@
         }
 
         void assertCreateScrollCaptureCallbackInternalCount(int count) {
-            assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal",
+            assertEquals("Unexpected number of calls to createScrollCaptureCallbackInternal",
                     count, mCreateScrollCaptureCallbackInternalCount);
         }
 
@@ -385,11 +397,11 @@
 
         @Override
         public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
-                Queue<ScrollCaptureTarget> targets) {
+                Consumer<ScrollCaptureTarget> results) {
             mDispatchScrollCaptureSearchNumCalls++;
             mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
             mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
-            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results);
         }
 
         @Override
@@ -401,13 +413,31 @@
         }
     }
 
+    static class CallbackStub implements ScrollCaptureCallback {
+
+        @Override
+        public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+                @NonNull Consumer<Rect> onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+        }
+
+        @Override
+        public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+                @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+                Consumer<Rect> onComplete) {
+        }
+
+        @Override
+        public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+        }
+    };
+
     public static final class MockViewGroup extends ViewGroup {
         private ScrollCaptureCallback mInternalCallback;
-        private int mDispatchScrollCaptureSearchNumCalls;
-        private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect;
-        private Point mDispatchScrollCaptureSearchLastWindowOffset;
-        private int mCreateScrollCaptureCallbackInternalCount;
-
 
         MockViewGroup(Context context) {
             this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
@@ -428,16 +458,10 @@
             mInternalCallback = internal;
         }
 
-        void assertDispatchScrollCaptureSearchCount(int count) {
-            assertEquals("Unexpected number of calls to dispatchScrollCaptureSearch",
-                    count, mDispatchScrollCaptureSearchNumCalls);
-        }
-
         @Override
         @Nullable
         public ScrollCaptureCallback createScrollCaptureCallbackInternal(Rect localVisibleRect,
                 Point offsetInWindow) {
-            mCreateScrollCaptureCallbackInternalCount++;
             return mInternalCallback;
         }
 
@@ -445,36 +469,5 @@
         protected void onLayout(boolean changed, int l, int t, int r, int b) {
             // We don't layout this view.
         }
-
-        void assertDispatchScrollCaptureCount(int count) {
-            assertEquals(count, mDispatchScrollCaptureSearchNumCalls);
-        }
-
-        void assertLastDispatchScrollCaptureArgs(Rect localVisibleRect, Point windowOffset) {
-            assertEquals("arg localVisibleRect to dispatchScrollCaptureCallback was incorrect.",
-                    localVisibleRect, mDispatchScrollCaptureSearchLastLocalVisibleRect);
-            assertEquals("arg windowOffset to dispatchScrollCaptureCallback was incorrect.",
-                    windowOffset, mDispatchScrollCaptureSearchLastWindowOffset);
-        }
-        void assertCreateScrollCaptureCallbackInternalCount(int count) {
-            assertEquals("Unexpected number of calls to createScrollCaptureCallackInternal",
-                    count, mCreateScrollCaptureCallbackInternalCount);
-        }
-
-        void reset() {
-            mDispatchScrollCaptureSearchNumCalls = 0;
-            mDispatchScrollCaptureSearchLastWindowOffset = null;
-            mDispatchScrollCaptureSearchLastLocalVisibleRect = null;
-            mCreateScrollCaptureCallbackInternalCount = 0;
-        }
-
-        @Override
-        public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
-                Queue<ScrollCaptureTarget> targets) {
-            mDispatchScrollCaptureSearchNumCalls++;
-            mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
-            mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
-            super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, targets);
-        }
     }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index c67174f..7746bc2 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -207,7 +207,7 @@
         final CountDownLatch latch = new CountDownLatch(1);
         mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
             @Override
-            public void onUnavailable() {
+            public void onScrollCaptureResponse(ScrollCaptureResponse response) {
                 latch.countDown();
             }
         });
@@ -220,6 +220,37 @@
     }
 
     /**
+     * Ensure scroll capture request handles a ViewRootImpl with no view tree.
+     */
+    @Test
+    public void requestScrollCapture_timeout() {
+        final View view = new View(mContext);
+        view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowManager.LayoutParams wmlp =
+                    new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+            // Set a fake token to bypass 'is your activity running' check
+            wmlp.token = new Binder();
+            view.setLayoutParams(wmlp);
+            mViewRootImpl.setView(view, wmlp, null);
+        });
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        mViewRootImpl.setScrollCaptureRequestTimeout(100);
+        mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+            @Override
+            public void onScrollCaptureResponse(ScrollCaptureResponse response) {
+                latch.countDown();
+            }
+        });
+        try {
+            if (!latch.await(2500, TimeUnit.MILLISECONDS)) {
+                fail("requestScrollCapture timeout did not occur");
+            }
+        } catch (InterruptedException e) { /* ignore */ }
+    }
+
+    /**
      * When window doesn't have focus, keys should be dropped.
      */
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 9cac7e7..ff728d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -77,6 +77,7 @@
  * bit FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest
  */
 @SmallTest
+@SkipPresubmit("b/180015146")
 @RunWith(AndroidJUnit4.class)
 public class BatteryStatsCpuTimesTest {
     @Mock
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
index 4b37dd2..24baa93 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsImplTest.java
@@ -73,6 +73,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testUpdateProcStateCpuTimes() {
         mBatteryStatsImpl.setOnBatteryInternal(true);
         mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0);
@@ -230,6 +231,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testCopyFromAllUidsCpuTimes() {
         mBatteryStatsImpl.setOnBatteryInternal(false);
         mBatteryStatsImpl.updateTimeBasesLocked(false, Display.STATE_ON, 0, 0);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 6652c64..931611e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -295,6 +295,7 @@
     }
 
     @SmallTest
+    @SkipPresubmit("b/180015146")
     public void testAlarmStartAndFinishLocked() throws Exception {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
@@ -332,6 +333,7 @@
     }
 
     @SmallTest
+    @SkipPresubmit("b/180015146")
     public void testAlarmStartAndFinishLocked_workSource() throws Exception {
         final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
index 3b27f18..dd814e6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSamplingTimerTest.java
@@ -56,6 +56,7 @@
     }
 
     @SmallTest
+    @SkipPresubmit("b/180015146")
     public void testEndSampleAndContinueWhenTimeOrCountDecreases() throws Exception {
         final MockClocks clocks = new MockClocks();
         final BatteryStatsImpl.TimeBase timeBase = Mockito.mock(BatteryStatsImpl.TimeBase.class);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 7b7cf65..d276bc3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -77,5 +77,4 @@
         com.android.internal.power.MeasuredEnergyStatsTest.class
     })
 public class BatteryStatsTests {
-}
-
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
index e7a1bca..e90bcb7 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsUserLifecycleTests.java
@@ -78,6 +78,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testNoCpuDataForRemovedUser() throws Exception {
         mIam.startUserInBackground(mTestUserId);
         waitUntilTrue("No uids for started user " + mTestUserId,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 9ef6288..23ea508 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -35,6 +35,7 @@
 import java.util.List;
 
 @SmallTest
+@SkipPresubmit("b/180015146")
 @RunWith(AndroidJUnit4.class)
 public class BatteryUsageStatsTest {
 
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index e559471..f6aa08b 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -43,6 +43,7 @@
             .setAveragePower(PowerProfile.POWER_BLUETOOTH_CONTROLLER_TX, 100.0);
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testTimerBasedModel() {
         setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
                         .getOrCreateBluetoothControllerActivityLocked(),
@@ -73,6 +74,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testReportedPowerBasedModel() {
         setDurationsAndPower(mStatsRule.getUidStats(Process.BLUETOOTH_UID)
                         .getOrCreateBluetoothControllerActivityLocked(),
diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
index a80f5a0..4fe7d70 100644
--- a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java
@@ -382,6 +382,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146 flakey")
     public void testCpuFreqTimes_stateFgService() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
             Log.w(TAG, "Skipping " + testName.getMethodName()
@@ -514,6 +515,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testCpuFreqTimes_trackingDisabled() throws Exception {
         if (!sCpuFreqTimesAvailable || !sPerProcStateTimesAvailable) {
             Log.w(TAG, "Skipping " + testName.getMethodName()
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 9cf0d37..e691beb 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -92,6 +92,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testTimerBasedModel() {
         when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
index a4ea892..f298f59 100644
--- a/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CustomMeasuredPowerCalculatorTest.java
@@ -42,6 +42,7 @@
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testMeasuredEnergyCopiedIntoBatteryConsumers() {
         final BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
         SparseLongArray uidEnergies = new SparseLongArray();
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
index 7dca0cb..177f348 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidUserSysTimeReaderTest.java
@@ -87,6 +87,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testThrottler() throws Exception {
         mReader = new KernelCpuUidUserSysTimeReader(
                 new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), true);
diff --git a/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java b/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java
new file mode 100644
index 0000000..d03ed66
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SkipPresubmit.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to skip a test from TEST_MAPPING presubmit. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface SkipPresubmit {
+    /** The optional reason why the test is ignored. */
+    String value() default "";
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index dfbf28b..b5282e9 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -78,6 +78,7 @@
     }
 
     @Test
+    @SkipPresubmit("b/180015146")
     public void testPowerProfileBasedModel() {
         when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8fd5d80..3900d7e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -406,8 +406,6 @@
         <permission name="android.permission.SET_WALLPAPER" />
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
-        <!-- Permissions required for Incremental CTS tests -->
-        <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
         <!-- Permission required to test system only camera devices. -->
         <permission name="android.permission.SYSTEM_CAMERA" />
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index f1f9a5f..e222570 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -153,6 +153,13 @@
      * resource bitmaps often are) the filtering will already have been
      * done.</p>
      *
+     * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
+     * accelerated drawing always uses bilinear sampling on scaled bitmaps,
+     * regardless of this flag. On devices running {@link Build.VERSION_CODES#Q}
+     * and above, this flag defaults to being set on a new {@code Paint}. It can
+     * be cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
+     *
+     * @see #Paint()
      * @see #Paint(int)
      * @see #setFlags(int)
      */
@@ -558,6 +565,12 @@
 
     /**
      * Create a new paint with default settings.
+     *
+     * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
+     * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
+     * On devices running {@link Build.VERSION_CODES#Q} and above,
+     * {@code FILTER_BITMAP_FLAG} is set by this constructor, and it can be
+     * cleared with {@link #setFlags} or {@link #setFilterBitmap}.</p>
      */
     public Paint() {
         this(0);
@@ -567,6 +580,13 @@
      * Create a new paint with the specified flags. Use setFlags() to change
      * these after the paint is created.
      *
+     * <p>On devices running {@link Build.VERSION_CODES#O} and below, hardware
+     * accelerated drawing always acts as if {@link #FILTER_BITMAP_FLAG} is set.
+     * On devices running {@link Build.VERSION_CODES#Q} and above,
+     * {@code FILTER_BITMAP_FLAG} is always set by this constructor, regardless
+     * of the value of {@code flags}. It can be cleared with {@link #setFlags} or
+     * {@link #setFilterBitmap}.</p>
+     *
      * @param flags initial flag bits, as if they were passed via setFlags().
      */
     public Paint(int flags) {
@@ -991,6 +1011,7 @@
      * device pixels. That is dependent on dithering and xfermodes.
      *
      * @see #setFilterBitmap(boolean) setFilterBitmap()
+     * @see #FILTER_BITMAP_FLAG
      */
     public final boolean isFilterBitmap() {
         return (getFlags() & FILTER_BITMAP_FLAG) != 0;
@@ -1004,6 +1025,7 @@
      *
      * @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
      *               flags, false to clear it.
+     * @see #FILTER_BITMAP_FLAG
      */
     public void setFilterBitmap(boolean filter) {
         nSetFilterBitmap(mNativePaint, filter);
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 476e4d7..6ac3821 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -24,6 +24,7 @@
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
 import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.Domain;
 import android.system.keystore2.IKeystoreService;
 import android.system.keystore2.KeyDescriptor;
 import android.system.keystore2.KeyEntryResponse;
@@ -157,6 +158,50 @@
     }
 
     /**
+     * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync
+     * with system/security/keystore-engine. Note: The prefix here includes the 0x which
+     * std::stringstream used in keystore-engine needs to identify the number as hex represented.
+     * Here we include it in the prefix, because Long#parseUnsignedLong does not understand it
+     * and gets the radix as explicit argument.
+     * @hide
+     */
+    private static final String KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX =
+            "ks2_keystore-engine_grant_id:0x";
+
+    /**
+     * This function turns a grant identifier into a specific string that is understood by the
+     * keystore-engine in system/security/keystore-engine. Is only used by VPN and WI-FI components
+     * to allow certain system components like racoon or vendor components like WPA supplicant
+     * to use keystore keys with boring ssl.
+     *
+     * @param grantId the grant id as returned by {@link #grant} in the {@code nspace} filed of
+     *                the resulting {@code KeyDescriptor}.
+     * @return The grant descriptor string.
+     * @hide
+     */
+    public static String makeKeystoreEngineGrantString(long grantId) {
+        return String.format("%s%016X", KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX, grantId);
+    }
+
+    /**
+     * Convenience function to turn a keystore engine grant string as returned by
+     * {@link #makeKeystoreEngineGrantString(long)} back into a grant KeyDescriptor.
+     *
+     * @param grantString As string returned by {@link #makeKeystoreEngineGrantString(long)}
+     * @return The grant key descriptor.
+     * @hide
+     */
+    public static KeyDescriptor keystoreEngineGrantString2KeyDescriptor(String grantString) {
+        KeyDescriptor key = new KeyDescriptor();
+        key.domain = Domain.GRANT;
+        key.nspace = Long.parseUnsignedLong(
+                grantString.substring(KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX.length()), 16);
+        key.alias = null;
+        key.blob = null;
+        return key;
+    }
+
+    /**
      * Create a grant that allows the grantee identified by {@code granteeUid} to use
      * the key specified by {@code descriptor} withint the restrictions given by
      * {@code accessVectore}.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index fb70cbe5..4bb8e9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -39,12 +39,12 @@
 import android.view.IWindowSessionCallback;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
+import android.view.ScrollCaptureResponse;
 import android.view.SurfaceControl;
-import android.view.SurfaceSession;
 import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewRootImpl;
 import android.view.WindowManager;
 import android.view.WindowlessWindowManager;
 import android.window.ClientWindowFrames;
@@ -371,7 +371,11 @@
         @Override
         public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
             try {
-                callbacks.onUnavailable();
+                callbacks.onScrollCaptureResponse(
+                        new ScrollCaptureResponse.Builder()
+                                .setDescription("Not Implemented")
+                                .build());
+
             } catch (RemoteException ex) {
                 // ignore
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index eb53783..5f003ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -71,16 +71,16 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
         testSpec.config.endRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
         testSpec.config.endRotation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 39c9484..d479208 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -76,17 +76,8 @@
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(isRotated)
-        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(isRotated)
-        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
-    }
+    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
+        Surface.ROTATION_0, testSpec.config.endRotation)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 41e2864..9011f1a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -18,7 +18,6 @@
 
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -125,7 +124,7 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun testAppLayerCoversFullScreen() {
         testSpec.assertLayersEnd {
@@ -133,11 +132,11 @@
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
similarity index 69%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 06ef79a..3e33176 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,12 +16,9 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
-import androidx.test.filters.RequiresDevice
-import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -35,21 +32,10 @@
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import org.junit.FixMethodOrder
 import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test Pip launch.
- * To run this test: `atest WMShellFlickerTests:PipToHomeTest`
- */
-@RequiresDevice
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) { configuration ->
             setup {
@@ -62,30 +48,27 @@
                     this.setRotation(Surface.ROTATION_0)
                 }
             }
-            transitions {
-                pipApp.closePipWindow(wmHelper)
-            }
         }
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun pipWindowBecomesInvisible() {
+    open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
                 .then()
@@ -95,7 +78,7 @@
 
     @Presubmit
     @Test
-    fun pipLayerBecomesInvisible() {
+    open fun pipLayerBecomesInvisible() {
         testSpec.assertLayers {
             this.isVisible(PIP_WINDOW_TITLE)
                 .then()
@@ -105,22 +88,22 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() =
+    open fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun noUncoveredRegions() =
+    open fun noUncoveredRegions() =
         testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() =
+    open fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
 
     @FlakyTest(bugId = 151179149)
     @Test
-    fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
+    open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
new file mode 100644
index 0000000..0408421
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.pip
+
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import org.junit.FixMethodOrder
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            transitions {
+                pipApp.closePipWindow(wmHelper)
+            }
+        }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
new file mode 100644
index 0000000..afaf33a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
+            transitions {
+                val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds
+                val pipCenterX = pipRegion.centerX()
+                val pipCenterY = pipRegion.centerY()
+                val displayCenterX = device.displayWidth / 2
+                device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5)
+            }
+        }
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible()
+
+    @Postsubmit
+    @Test
+    override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible()
+
+    @Postsubmit
+    @Test
+    override fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Postsubmit
+    @Test
+    override fun noUncoveredRegions() = super.noUncoveredRegions()
+
+    @Postsubmit
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
new file mode 100644
index 0000000..4c95da2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.view.Surface
+import androidx.test.filters.RequiresDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.google.common.truth.Truth
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest WMShellFlickerTests:PipMovesInAllApps`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
+    private val taplInstrumentation = LauncherInstrumentation()
+
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = buildTransition(eachRun = false) {
+            teardown {
+                eachRun {
+                    taplInstrumentation.pressHome()
+                }
+            }
+            transitions {
+                taplInstrumentation.pressHome().switchToAllApps()
+                wmHelper.waitForAppTransitionIdle()
+            }
+        }
+
+    @Postsubmit
+    @Test
+    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+    @Postsubmit
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) }
+
+    @Postsubmit
+    @Test
+    fun pipWindowMovesUp() = testSpec.assertWmEnd {
+        val initialState = this.trace?.first()?.wmState
+            ?: error("Trace should not be empty")
+        val startPos = initialState.pinnedWindows.first().frame
+        val currPos = this.wmState.pinnedWindows.first().frame
+        val subject = Truth.assertWithMessage("Pip should have moved up")
+        subject.that(currPos.top).isGreaterThan(startPos.top)
+        subject.that(currPos.bottom).isGreaterThan(startPos.bottom)
+        subject.that(currPos.left).isEqualTo(startPos.left)
+        subject.that(currPos.right).isEqualTo(startPos.right)
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTestParameter> {
+            return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+                supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5)
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 37b49c7..df835d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -88,11 +88,11 @@
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 704fd1d..1bb1d28 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -66,7 +66,7 @@
             }
         }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
@@ -107,12 +107,12 @@
         }
     }
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun noUncoveredRegions() =
         testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 1fcc7d0..7916ce5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -123,11 +123,11 @@
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @FlakyTest(bugId = 140855415)
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java
index f1eb8fc..6633d24 100644
--- a/location/java/android/location/GnssAntennaInfo.java
+++ b/location/java/android/location/GnssAntennaInfo.java
@@ -39,11 +39,7 @@
 
     /**
      * Used for receiving GNSS antenna info from the GNSS engine.
-     *
-     * @deprecated Prefer to use a broadcast receiver for
-     * {@link LocationManager#ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
-    @Deprecated
     public interface Listener {
         /**
          * Invoked on a change to GNSS antenna info.
diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl
new file mode 100644
index 0000000..3cceea3
--- /dev/null
+++ b/location/java/android/location/IGnssAntennaInfoListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.GnssAntennaInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IGnssAntennaInfoListener {
+    void onGnssAntennaInfoChanged(in List<GnssAntennaInfo> antennaInfos);
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 38b48e9..6fa6536 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -26,6 +26,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
 import android.location.IGeocodeListener;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
 import android.location.IGnssNavigationMessageListener;
@@ -92,6 +93,9 @@
     void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag, String listenerId);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
+    void addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, String packageName, @nullable String attributionTag, String listenerId);
+    void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
+
     void addProviderRequestListener(in IProviderRequestListener listener);
     void removeProviderRequestListener(in IProviderRequestListener listener);
 
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 95bae5a..e73e915 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -43,10 +43,8 @@
 import android.compat.Compatibility;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.location.provider.IProviderRequestListener;
 import android.location.provider.ProviderProperties;
@@ -349,28 +347,6 @@
     public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES";
 
     /**
-     * Broadcast intent action when GNSS antenna infos change. Includes an intent extra,
-     * {@link #EXTRA_GNSS_ANTENNA_INFOS}, with an ArrayList of the new {@link GnssAntennaInfo}. This
-     * may be read via {@link android.content.Intent#getParcelableArrayListExtra(String)}.
-     *
-     * @see #EXTRA_GNSS_ANTENNA_INFOS
-     * @see #getGnssAntennaInfos()
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED =
-            "android.location.action.GNSS_ANTENNA_INFOS_CHANGED";
-
-    /**
-     * Intent extra included with {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED} broadcasts, containing
-     * the new ArrayList of {@link GnssAntennaInfo}. This may be read via
-     * {@link android.content.Intent#getParcelableArrayListExtra(String)}.
-     *
-     * @see #ACTION_GNSS_ANTENNA_INFOS_CHANGED
-     */
-    public static final String EXTRA_GNSS_ANTENNA_INFOS =
-            "android.location.extra.GNSS_ANTENNA_INFOS";
-
-    /**
      * Broadcast intent action for Settings app to inject a footer at the bottom of location
      * settings. This is for use only by apps that are included in the system image.
      *
@@ -1967,7 +1943,7 @@
      *
      * @param provider the provider name
      * @param properties the provider properties
-     * @param locationTags the attribution tags for accessing location from the provider
+     * @param extraAttributionTags additional attribution tags associated with this provider
      *
      * @throws IllegalArgumentException if provider is null
      * @throws IllegalArgumentException if properties is null
@@ -1976,13 +1952,14 @@
      * allowed} for your app.
      */
     public void addTestProvider(@NonNull String provider, @NonNull ProviderProperties properties,
-            @NonNull Set<String> locationTags) {
+            @NonNull Set<String> extraAttributionTags) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
         Preconditions.checkArgument(properties != null, "invalid null properties");
-        Preconditions.checkArgument(locationTags != null, "invalid null location tags");
+        Preconditions.checkArgument(extraAttributionTags != null,
+                "invalid null extra attribution tags");
 
         try {
-            mService.addTestProvider(provider, properties, new ArrayList<>(locationTags),
+            mService.addTestProvider(provider, properties, new ArrayList<>(extraAttributionTags),
                     mContext.getOpPackageName(), mContext.getFeatureId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2659,10 +2636,11 @@
     }
 
     /**
-     * Registers a GNSS antenna info listener. GNSS antenna info updates will only be received while
-     * the {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
+     * Registers a GNSS antenna info listener that will receive all changes to antenna info. Use
+     * {@link #getGnssAntennaInfos()} to get current antenna info.
      *
-     * <p>Not all GNSS chipsets support antenna info updates, see {@link #getGnssCapabilities()}.
+     * <p>Not all GNSS chipsets support antenna info updates, see {@link #getGnssCapabilities()}. If
+     * unsupported, the listener will never be invoked.
      *
      * <p>Prior to Android S, this requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
      * permission.
@@ -2673,10 +2651,7 @@
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if listener is null
-     *
-     * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
-    @Deprecated
     public boolean registerAntennaInfoListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssAntennaInfo.Listener listener) {
@@ -2686,13 +2661,10 @@
     }
 
     /**
-     * Unregisters a GNSS Antenna Info listener.
+     * Unregisters a GNSS antenna info listener.
      *
      * @param listener a {@link GnssAntennaInfo.Listener} object to remove
-     *
-     * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
-    @Deprecated
     public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
         GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener);
     }
@@ -3009,14 +2981,17 @@
         }
 
         @Override
-        protected void registerTransport(GnssAntennaInfoTransport transport) {
-            transport.getContext().registerReceiver(transport,
-                    new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
+        protected void registerTransport(GnssAntennaInfoTransport transport)
+                throws RemoteException {
+            getService().addGnssAntennaInfoListener(transport, transport.getPackage(),
+                    transport.getAttributionTag(),
+                    AppOpsManager.toReceiverId(transport.getListener()));
         }
 
         @Override
-        protected void unregisterTransport(GnssAntennaInfoTransport transport) {
-            transport.getContext().unregisterReceiver(transport);
+        protected void unregisterTransport(GnssAntennaInfoTransport transport)
+                throws RemoteException {
+            getService().removeGnssAntennaInfoListener(transport);
         }
     }
 
@@ -3376,11 +3351,12 @@
         }
     }
 
-    private static class GnssAntennaInfoTransport extends BroadcastReceiver implements
+    private static class GnssAntennaInfoTransport extends IGnssAntennaInfoListener.Stub implements
             ListenerTransport<GnssAntennaInfo.Listener> {
 
         private final Executor mExecutor;
-        private final Context mContext;
+        private final String mPackageName;
+        private final String mAttributionTag;
 
         private volatile @Nullable GnssAntennaInfo.Listener mListener;
 
@@ -3389,12 +3365,17 @@
             Preconditions.checkArgument(executor != null, "invalid null executor");
             Preconditions.checkArgument(listener != null, "invalid null listener");
             mExecutor = executor;
-            mContext = context;
+            mPackageName = context.getPackageName();
+            mAttributionTag = context.getAttributionTag();
             mListener = listener;
         }
 
-        public Context getContext() {
-            return mContext;
+        public String getPackage() {
+            return mPackageName;
+        }
+
+        public String getAttributionTag() {
+            return mAttributionTag;
         }
 
         @Override
@@ -3408,12 +3389,8 @@
         }
 
         @Override
-        public void onReceive(Context context, Intent intent) {
-            ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
-                    EXTRA_GNSS_ANTENNA_INFOS);
-            if (infos != null) {
-                execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos));
-            }
+        public void onGnssAntennaInfoChanged(List<GnssAntennaInfo> antennaInfos) {
+            execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(antennaInfos));
         }
     }
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb1dbd4..7e729d8 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1264,6 +1264,8 @@
      *
      * @param usage one of the {@link AudioAttributes} usage constants
      * @return string representing the {@link AudioAttributes} usage constant passed as a parameter
+     *
+     * @hide
      */
     @NonNull
     public static String usageToString(@AttributeSdkUsage int usage) {
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4d7ed11..06d0eb0 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -19,6 +19,7 @@
 import static android.media.Utils.intersectSortedDistinctRanges;
 import static android.media.Utils.sortDistinctRanges;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -1133,6 +1134,7 @@
          * in the ranges returned by {@link #getInputChannelCountRanges}
          *
          */
+        @IntRange(from = 1, to = 255)
         public int getMaxInputChannelCount() {
             int overall_max = 0;
             for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
@@ -1151,6 +1153,7 @@
          * This returns the lowest channel count in the ranges returned by
          * {@link #getInputChannelCountRanges}.
          */
+        @IntRange(from = 1, to = 255)
         public int getMinInputChannelCount() {
             int overall_min = MAX_INPUT_CHANNEL_COUNT;
             for (int i = mInputChannelRanges.length - 1; i >= 0; i--) {
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index 6a351d3..e5d0370 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1 +1,2 @@
+ytai@google.com
 elaurent@google.com
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 042d02f..1d2657a 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -144,11 +144,6 @@
         native_remove_storage(storage.getStorageId());
     }
 
-    public static void configure(boolean usePtp) {
-        native_configure(usePtp);
-    }
-
-    public static native final void native_configure(boolean usePtp);
     private native final void native_setup(
             MtpDatabase database,
             FileDescriptor controlFd,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java
new file mode 100644
index 0000000..3a4bec0
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/AudioAttributesUnitTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+
+import static org.junit.Assert.assertEquals;
+
+import android.media.AudioAttributes;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AudioAttributesUnitTest {
+
+    @Test
+    public void testUsageToString_returnCorrectStrings() {
+        assertEquals("USAGE_UNKNOWN", AudioAttributes.usageToString(AudioAttributes.USAGE_UNKNOWN));
+        assertEquals("USAGE_MEDIA", AudioAttributes.usageToString(AudioAttributes.USAGE_MEDIA));
+        assertEquals("USAGE_VOICE_COMMUNICATION",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_VOICE_COMMUNICATION));
+        assertEquals("USAGE_VOICE_COMMUNICATION_SIGNALLING",
+                AudioAttributes.usageToString(
+                        AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING));
+        assertEquals("USAGE_ALARM", AudioAttributes.usageToString(AudioAttributes.USAGE_ALARM));
+        assertEquals("USAGE_NOTIFICATION",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION));
+        assertEquals("USAGE_NOTIFICATION_RINGTONE",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION_RINGTONE));
+        assertEquals("USAGE_NOTIFICATION_COMMUNICATION_REQUEST",
+                AudioAttributes.usageToString(
+                        AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST));
+        assertEquals("USAGE_NOTIFICATION_COMMUNICATION_INSTANT",
+                AudioAttributes.usageToString(
+                        AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT));
+        assertEquals("USAGE_NOTIFICATION_COMMUNICATION_DELAYED",
+                AudioAttributes.usageToString(
+                        AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED));
+        assertEquals("USAGE_NOTIFICATION_EVENT",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_NOTIFICATION_EVENT));
+        assertEquals("USAGE_ASSISTANCE_ACCESSIBILITY",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY));
+        assertEquals("USAGE_ASSISTANCE_NAVIGATION_GUIDANCE",
+                AudioAttributes.usageToString(
+                        AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE));
+        assertEquals("USAGE_ASSISTANCE_SONIFICATION",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION));
+        assertEquals("USAGE_GAME", AudioAttributes.usageToString(AudioAttributes.USAGE_GAME));
+        assertEquals("USAGE_ASSISTANT",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_ASSISTANT));
+        assertEquals("USAGE_CALL_ASSISTANT",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_CALL_ASSISTANT));
+        assertEquals("USAGE_EMERGENCY",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_EMERGENCY));
+        assertEquals("USAGE_SAFETY", AudioAttributes.usageToString(AudioAttributes.USAGE_SAFETY));
+        assertEquals("USAGE_VEHICLE_STATUS",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_VEHICLE_STATUS));
+        assertEquals("USAGE_ANNOUNCEMENT",
+                AudioAttributes.usageToString(AudioAttributes.USAGE_ANNOUNCEMENT));
+    }
+
+    @Test
+    public void testUsageToString_unknownUsage() {
+        assertEquals("unknown usage -1", AudioAttributes.usageToString(-1));
+    }
+}
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index c7e261c..86b85e83 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -58,3 +58,28 @@
         "//packages/modules/Connectivity:__subpackages__",
     ],
 }
+
+java_sdk_library {
+    name: "framework-connectivity",
+    api_only: true,
+    defaults: ["framework-module-defaults"],
+    // TODO: build against module API
+    platform_apis: true,
+    srcs: [
+        ":framework-connectivity-sources",
+    ],
+    aidl: {
+        include_dirs: [
+            // Include directories for parcelables that are part of the stable API, and need a
+            // one-line "parcelable X" .aidl declaration to be used in AIDL interfaces.
+            // TODO(b/180293679): remove these dependencies as they should not be necessary once
+            // the module builds against API (the parcelable declarations exist in framework.aidl)
+            "frameworks/base/core/java", // For framework parcelables
+            "frameworks/native/aidl/binder", // For PersistableBundle.aidl
+        ],
+    },
+    libs: [
+        "unsupportedappusage",
+    ],
+    permitted_packages: ["android.net", "com.android.connectivity.aidl"],
+}
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
new file mode 100644
index 0000000..31b8fc8
--- /dev/null
+++ b/packages/Connectivity/framework/api/current.txt
@@ -0,0 +1,470 @@
+// Signature format: 2.0
+package android.net {
+
+  public class CaptivePortal implements android.os.Parcelable {
+    method public int describeContents();
+    method public void ignoreNetwork();
+    method public void reportCaptivePortalDismissed();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortal> CREATOR;
+  }
+
+  public class ConnectivityDiagnosticsManager {
+    method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+    method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
+  }
+
+  public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback();
+    method public void onConnectivityReportAvailable(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityReport);
+    method public void onDataStallSuspected(@NonNull android.net.ConnectivityDiagnosticsManager.DataStallReport);
+    method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
+  }
+
+  public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
+    ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+    method public int describeContents();
+    method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
+    method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public long getReportTimestamp();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
+    field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+    field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+    field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+    field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+    field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+    field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+    field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+    field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+    field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+    field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
+  }
+
+  public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
+    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
+    method public int describeContents();
+    method public int getDetectionMethod();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
+    method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+    method public long getReportTimestamp();
+    method @NonNull public android.os.PersistableBundle getStallDetails();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+    field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+    field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+    field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+    field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+    field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+  }
+
+  public class ConnectivityManager {
+    method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+    method public boolean bindProcessToNetwork(@Nullable android.net.Network);
+    method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
+    method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
+    method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
+    method @Deprecated public boolean getBackgroundDataSetting();
+    method @Nullable public android.net.Network getBoundNetworkForProcess();
+    method public int getConnectionOwnerUid(int, @NonNull java.net.InetSocketAddress, @NonNull java.net.InetSocketAddress);
+    method @Nullable public android.net.ProxyInfo getDefaultProxy();
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.LinkProperties getLinkProperties(@Nullable android.net.Network);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getMultipathPreference(@Nullable android.net.Network);
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkCapabilities getNetworkCapabilities(@Nullable android.net.Network);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(int);
+    method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getNetworkInfo(@Nullable android.net.Network);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public int getNetworkPreference();
+    method @Nullable public byte[] getNetworkWatchlistConfigHash();
+    method @Deprecated @Nullable public static android.net.Network getProcessDefaultNetwork();
+    method public int getRestrictBackgroundStatus();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public boolean isActiveNetworkMetered();
+    method public boolean isDefaultNetworkActive();
+    method @Deprecated public static boolean isNetworkTypeValid(int);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerNetworkCallback(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+    method public void releaseNetworkRequest(@NonNull android.app.PendingIntent);
+    method public void removeDefaultNetworkActiveListener(@NonNull android.net.ConnectivityManager.OnNetworkActiveListener);
+    method @Deprecated public void reportBadNetwork(@Nullable android.net.Network);
+    method public void reportNetworkConnectivity(@Nullable android.net.Network, boolean);
+    method public boolean requestBandwidthUpdate(@NonNull android.net.Network);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler, int);
+    method public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.app.PendingIntent);
+    method @Deprecated public void setNetworkPreference(int);
+    method @Deprecated public static boolean setProcessDefaultNetwork(@Nullable android.net.Network);
+    method public void unregisterNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback);
+    method public void unregisterNetworkCallback(@NonNull android.app.PendingIntent);
+    field @Deprecated public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+    field public static final String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
+    field @Deprecated public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+    field @Deprecated public static final int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
+    field public static final String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
+    field @Deprecated public static final String EXTRA_EXTRA_INFO = "extraInfo";
+    field @Deprecated public static final String EXTRA_IS_FAILOVER = "isFailover";
+    field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
+    field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
+    field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
+    field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
+    field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
+    field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+    field public static final String EXTRA_REASON = "reason";
+    field public static final int MULTIPATH_PREFERENCE_HANDOVER = 1; // 0x1
+    field public static final int MULTIPATH_PREFERENCE_PERFORMANCE = 4; // 0x4
+    field public static final int MULTIPATH_PREFERENCE_RELIABILITY = 2; // 0x2
+    field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
+    field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
+    field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
+    field @Deprecated public static final int TYPE_BLUETOOTH = 7; // 0x7
+    field @Deprecated public static final int TYPE_DUMMY = 8; // 0x8
+    field @Deprecated public static final int TYPE_ETHERNET = 9; // 0x9
+    field @Deprecated public static final int TYPE_MOBILE = 0; // 0x0
+    field @Deprecated public static final int TYPE_MOBILE_DUN = 4; // 0x4
+    field @Deprecated public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
+    field @Deprecated public static final int TYPE_MOBILE_MMS = 2; // 0x2
+    field @Deprecated public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+    field @Deprecated public static final int TYPE_VPN = 17; // 0x11
+    field @Deprecated public static final int TYPE_WIFI = 1; // 0x1
+    field @Deprecated public static final int TYPE_WIMAX = 6; // 0x6
+  }
+
+  public static class ConnectivityManager.NetworkCallback {
+    ctor public ConnectivityManager.NetworkCallback();
+    method public void onAvailable(@NonNull android.net.Network);
+    method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
+    method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
+    method public void onLinkPropertiesChanged(@NonNull android.net.Network, @NonNull android.net.LinkProperties);
+    method public void onLosing(@NonNull android.net.Network, int);
+    method public void onLost(@NonNull android.net.Network);
+    method public void onUnavailable();
+  }
+
+  public static interface ConnectivityManager.OnNetworkActiveListener {
+    method public void onNetworkActive();
+  }
+
+  public class DhcpInfo implements android.os.Parcelable {
+    ctor public DhcpInfo();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpInfo> CREATOR;
+    field public int dns1;
+    field public int dns2;
+    field public int gateway;
+    field public int ipAddress;
+    field public int leaseDuration;
+    field public int netmask;
+    field public int serverAddress;
+  }
+
+  public final class DnsResolver {
+    method @NonNull public static android.net.DnsResolver getInstance();
+    method public void query(@Nullable android.net.Network, @NonNull String, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+    method public void query(@Nullable android.net.Network, @NonNull String, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super java.util.List<java.net.InetAddress>>);
+    method public void rawQuery(@Nullable android.net.Network, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+    method public void rawQuery(@Nullable android.net.Network, @NonNull String, int, int, int, @NonNull java.util.concurrent.Executor, @Nullable android.os.CancellationSignal, @NonNull android.net.DnsResolver.Callback<? super byte[]>);
+    field public static final int CLASS_IN = 1; // 0x1
+    field public static final int ERROR_PARSE = 0; // 0x0
+    field public static final int ERROR_SYSTEM = 1; // 0x1
+    field public static final int FLAG_EMPTY = 0; // 0x0
+    field public static final int FLAG_NO_CACHE_LOOKUP = 4; // 0x4
+    field public static final int FLAG_NO_CACHE_STORE = 2; // 0x2
+    field public static final int FLAG_NO_RETRY = 1; // 0x1
+    field public static final int TYPE_A = 1; // 0x1
+    field public static final int TYPE_AAAA = 28; // 0x1c
+  }
+
+  public static interface DnsResolver.Callback<T> {
+    method public void onAnswer(@NonNull T, int);
+    method public void onError(@NonNull android.net.DnsResolver.DnsException);
+  }
+
+  public static class DnsResolver.DnsException extends java.lang.Exception {
+    field public final int code;
+  }
+
+  public class InetAddresses {
+    method public static boolean isNumericAddress(@NonNull String);
+    method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
+  }
+
+  public final class IpPrefix implements android.os.Parcelable {
+    method public boolean contains(@NonNull java.net.InetAddress);
+    method public int describeContents();
+    method @NonNull public java.net.InetAddress getAddress();
+    method @IntRange(from=0, to=128) public int getPrefixLength();
+    method @NonNull public byte[] getRawAddress();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
+  }
+
+  public class LinkAddress implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.net.InetAddress getAddress();
+    method public int getFlags();
+    method @IntRange(from=0, to=128) public int getPrefixLength();
+    method public int getScope();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkAddress> CREATOR;
+  }
+
+  public final class LinkProperties implements android.os.Parcelable {
+    ctor public LinkProperties();
+    method public boolean addRoute(@NonNull android.net.RouteInfo);
+    method public void clear();
+    method public int describeContents();
+    method @Nullable public java.net.Inet4Address getDhcpServerAddress();
+    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+    method @Nullable public String getDomains();
+    method @Nullable public android.net.ProxyInfo getHttpProxy();
+    method @Nullable public String getInterfaceName();
+    method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
+    method public int getMtu();
+    method @Nullable public android.net.IpPrefix getNat64Prefix();
+    method @Nullable public String getPrivateDnsServerName();
+    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
+    method public boolean isPrivateDnsActive();
+    method public boolean isWakeOnLanSupported();
+    method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
+    method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+    method public void setDomains(@Nullable String);
+    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+    method public void setInterfaceName(@Nullable String);
+    method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method public void setMtu(int);
+    method public void setNat64Prefix(@Nullable android.net.IpPrefix);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
+  }
+
+  public final class MacAddress implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public static android.net.MacAddress fromBytes(@NonNull byte[]);
+    method @NonNull public static android.net.MacAddress fromString(@NonNull String);
+    method public int getAddressType();
+    method @Nullable public java.net.Inet6Address getLinkLocalIpv6FromEui48Mac();
+    method public boolean isLocallyAssigned();
+    method public boolean matches(@NonNull android.net.MacAddress, @NonNull android.net.MacAddress);
+    method @NonNull public byte[] toByteArray();
+    method @NonNull public String toOuiString();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.net.MacAddress BROADCAST_ADDRESS;
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
+    field public static final int TYPE_BROADCAST = 3; // 0x3
+    field public static final int TYPE_MULTICAST = 2; // 0x2
+    field public static final int TYPE_UNICAST = 1; // 0x1
+  }
+
+  public class Network implements android.os.Parcelable {
+    method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
+    method public void bindSocket(java.net.Socket) throws java.io.IOException;
+    method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
+    method public int describeContents();
+    method public static android.net.Network fromNetworkHandle(long);
+    method public java.net.InetAddress[] getAllByName(String) throws java.net.UnknownHostException;
+    method public java.net.InetAddress getByName(String) throws java.net.UnknownHostException;
+    method public long getNetworkHandle();
+    method public javax.net.SocketFactory getSocketFactory();
+    method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities();
+    ctor public NetworkCapabilities(android.net.NetworkCapabilities);
+    method public int describeContents();
+    method public int getLinkDownstreamBandwidthKbps();
+    method public int getLinkUpstreamBandwidthKbps();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+    method public int getOwnerUid();
+    method public int getSignalStrength();
+    method @Nullable public android.net.TransportInfo getTransportInfo();
+    method public boolean hasCapability(int);
+    method public boolean hasTransport(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
+    field public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17; // 0x11
+    field public static final int NET_CAPABILITY_CBS = 5; // 0x5
+    field public static final int NET_CAPABILITY_DUN = 2; // 0x2
+    field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+    field public static final int NET_CAPABILITY_ENTERPRISE = 29; // 0x1d
+    field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
+    field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
+    field public static final int NET_CAPABILITY_IA = 7; // 0x7
+    field public static final int NET_CAPABILITY_IMS = 4; // 0x4
+    field public static final int NET_CAPABILITY_INTERNET = 12; // 0xc
+    field public static final int NET_CAPABILITY_MCX = 23; // 0x17
+    field public static final int NET_CAPABILITY_MMS = 0; // 0x0
+    field public static final int NET_CAPABILITY_NOT_CONGESTED = 20; // 0x14
+    field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
+    field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+    field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+    field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
+    field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
+    field public static final int NET_CAPABILITY_RCS = 8; // 0x8
+    field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
+    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
+    field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
+    field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
+    field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
+    field public static final int NET_CAPABILITY_XCAP = 9; // 0x9
+    field public static final int SIGNAL_STRENGTH_UNSPECIFIED = -2147483648; // 0x80000000
+    field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
+    field public static final int TRANSPORT_CELLULAR = 0; // 0x0
+    field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
+    field public static final int TRANSPORT_VPN = 4; // 0x4
+    field public static final int TRANSPORT_WIFI = 1; // 0x1
+    field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
+  }
+
+  @Deprecated public class NetworkInfo implements android.os.Parcelable {
+    ctor @Deprecated public NetworkInfo(int, int, @Nullable String, @Nullable String);
+    method @Deprecated public int describeContents();
+    method @Deprecated @NonNull public android.net.NetworkInfo.DetailedState getDetailedState();
+    method @Deprecated public String getExtraInfo();
+    method @Deprecated public String getReason();
+    method @Deprecated public android.net.NetworkInfo.State getState();
+    method @Deprecated public int getSubtype();
+    method @Deprecated public String getSubtypeName();
+    method @Deprecated public int getType();
+    method @Deprecated public String getTypeName();
+    method @Deprecated public boolean isAvailable();
+    method @Deprecated public boolean isConnected();
+    method @Deprecated public boolean isConnectedOrConnecting();
+    method @Deprecated public boolean isFailover();
+    method @Deprecated public boolean isRoaming();
+    method @Deprecated public void setDetailedState(@NonNull android.net.NetworkInfo.DetailedState, @Nullable String, @Nullable String);
+    method @Deprecated public void writeToParcel(android.os.Parcel, int);
+    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
+  }
+
+  @Deprecated public enum NetworkInfo.DetailedState {
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState AUTHENTICATING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState BLOCKED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CAPTIVE_PORTAL_CHECK;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState CONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState DISCONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState FAILED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState IDLE;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState OBTAINING_IPADDR;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SCANNING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState SUSPENDED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.DetailedState VERIFYING_POOR_LINK;
+  }
+
+  @Deprecated public enum NetworkInfo.State {
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State CONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State DISCONNECTING;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State SUSPENDED;
+    enum_constant @Deprecated public static final android.net.NetworkInfo.State UNKNOWN;
+  }
+
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean canBeSatisfiedBy(@Nullable android.net.NetworkCapabilities);
+    method public int describeContents();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
+    method public boolean hasCapability(int);
+    method public boolean hasTransport(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
+  }
+
+  public static class NetworkRequest.Builder {
+    ctor public NetworkRequest.Builder();
+    method public android.net.NetworkRequest.Builder addCapability(int);
+    method public android.net.NetworkRequest.Builder addTransportType(int);
+    method public android.net.NetworkRequest build();
+    method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
+    method public android.net.NetworkRequest.Builder removeCapability(int);
+    method public android.net.NetworkRequest.Builder removeTransportType(int);
+    method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
+    method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+  }
+
+  public final class Proxy {
+    ctor public Proxy();
+    method @Deprecated public static String getDefaultHost();
+    method @Deprecated public static int getDefaultPort();
+    method @Deprecated public static String getHost(android.content.Context);
+    method @Deprecated public static int getPort(android.content.Context);
+    field @Deprecated public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+    field public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
+  }
+
+  public class ProxyInfo implements android.os.Parcelable {
+    ctor public ProxyInfo(@Nullable android.net.ProxyInfo);
+    method public static android.net.ProxyInfo buildDirectProxy(String, int);
+    method public static android.net.ProxyInfo buildDirectProxy(String, int, java.util.List<java.lang.String>);
+    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
+    method @NonNull public static android.net.ProxyInfo buildPacProxy(@NonNull android.net.Uri, int);
+    method public int describeContents();
+    method public String[] getExclusionList();
+    method public String getHost();
+    method public android.net.Uri getPacFileUrl();
+    method public int getPort();
+    method public boolean isValid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.ProxyInfo> CREATOR;
+  }
+
+  public final class RouteInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.IpPrefix getDestination();
+    method @Nullable public java.net.InetAddress getGateway();
+    method @Nullable public String getInterface();
+    method public boolean hasGateway();
+    method public boolean isDefaultRoute();
+    method public boolean matches(java.net.InetAddress);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.RouteInfo> CREATOR;
+  }
+
+  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+    method public final void close();
+    method public final void start(@IntRange(from=0xa, to=0xe10) int);
+    method public final void stop();
+    field public static final int ERROR_HARDWARE_ERROR = -31; // 0xffffffe1
+    field public static final int ERROR_INSUFFICIENT_RESOURCES = -32; // 0xffffffe0
+    field public static final int ERROR_INVALID_INTERVAL = -24; // 0xffffffe8
+    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+    field public static final int ERROR_INVALID_NETWORK = -20; // 0xffffffec
+    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+    field public static final int ERROR_INVALID_SOCKET = -25; // 0xffffffe7
+    field public static final int ERROR_SOCKET_NOT_IDLE = -26; // 0xffffffe6
+    field public static final int ERROR_UNSUPPORTED = -30; // 0xffffffe2
+  }
+
+  public static class SocketKeepalive.Callback {
+    ctor public SocketKeepalive.Callback();
+    method public void onDataReceived();
+    method public void onError(int);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public interface TransportInfo {
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/lint-baseline.txt b/packages/Connectivity/framework/api/lint-baseline.txt
new file mode 100644
index 0000000..2f4004a
--- /dev/null
+++ b/packages/Connectivity/framework/api/lint-baseline.txt
@@ -0,0 +1,4 @@
+// Baseline format: 1.0
+VisiblySynchronized: android.net.NetworkInfo#toString():
+    Internal locks must not be exposed (synchronizing on this or class is still
+    externally observable): method android.net.NetworkInfo.toString()
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
new file mode 100644
index 0000000..3af855e
--- /dev/null
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -0,0 +1,66 @@
+// Signature format: 2.0
+package android.net {
+
+  public final class ConnectivityFrameworkInitializer {
+    method public static void registerServiceWrappers();
+  }
+
+  public class ConnectivityManager {
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+  }
+
+  public final class NetworkAgentConfig implements android.os.Parcelable {
+    method @Nullable public String getSubscriberId();
+  }
+
+  public static final class NetworkAgentConfig.Builder {
+    method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    field public static final int TRANSPORT_TEST = 7; // 0x7
+  }
+
+  public final class Proxy {
+    method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
+  }
+
+  public final class TcpRepairWindow {
+    ctor public TcpRepairWindow(int, int, int, int, int, int);
+    field public final int maxWindow;
+    field public final int rcvWnd;
+    field public final int rcvWndScale;
+    field public final int rcvWup;
+    field public final int sndWl1;
+    field public final int sndWnd;
+  }
+
+  public final class TestNetworkInterface implements android.os.Parcelable {
+    ctor public TestNetworkInterface(@NonNull android.os.ParcelFileDescriptor, @NonNull String);
+    method public int describeContents();
+    method @NonNull public android.os.ParcelFileDescriptor getFileDescriptor();
+    method @NonNull public String getInterfaceName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TestNetworkInterface> CREATOR;
+  }
+
+  public class TestNetworkManager {
+    method @NonNull public android.net.TestNetworkInterface createTapInterface();
+    method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+    method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+    method public void teardownTestNetwork(@NonNull android.net.Network);
+    field public static final String TEST_TAP_PREFIX = "testtap";
+  }
+
+  public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
+    ctor public VpnTransportInfo(int);
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+    field public final int type;
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/module-lib-removed.txt b/packages/Connectivity/framework/api/module-lib-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Connectivity/framework/api/module-lib-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Connectivity/framework/api/removed.txt b/packages/Connectivity/framework/api/removed.txt
new file mode 100644
index 0000000..303a1e61
--- /dev/null
+++ b/packages/Connectivity/framework/api/removed.txt
@@ -0,0 +1,11 @@
+// Signature format: 2.0
+package android.net {
+
+  public class ConnectivityManager {
+    method @Deprecated public boolean requestRouteToHost(int, int);
+    method @Deprecated public int startUsingNetworkFeature(int, String);
+    method @Deprecated public int stopUsingNetworkFeature(int, String);
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
new file mode 100644
index 0000000..41ebc57
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -0,0 +1,407 @@
+// Signature format: 2.0
+package android.net {
+
+  public class CaptivePortal implements android.os.Parcelable {
+    method public void logEvent(int, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
+    method public void useNetwork();
+    field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
+    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
+    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
+    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
+  }
+
+  public final class CaptivePortalData implements android.os.Parcelable {
+    method public int describeContents();
+    method public long getByteLimit();
+    method public long getExpiryTimeMillis();
+    method public long getRefreshTimeMillis();
+    method @Nullable public android.net.Uri getUserPortalUrl();
+    method public int getUserPortalUrlSource();
+    method @Nullable public String getVenueFriendlyName();
+    method @Nullable public android.net.Uri getVenueInfoUrl();
+    method public int getVenueInfoUrlSource();
+    method public boolean isCaptive();
+    method public boolean isSessionExtendable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+  }
+
+  public static class CaptivePortalData.Builder {
+    ctor public CaptivePortalData.Builder();
+    ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+    method @NonNull public android.net.CaptivePortalData build();
+    method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+    method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri, int);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueFriendlyName(@Nullable String);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+    method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri, int);
+  }
+
+  public class ConnectivityManager {
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
+    method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE) public void setOemNetworkPreference(@NonNull android.net.OemNetworkPreferences, @Nullable java.util.concurrent.Executor, @Nullable android.net.ConnectivityManager.OnSetOemNetworkPreferenceListener);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+    method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+    method public void unregisterQosCallback(@NonNull android.net.QosCallback);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
+    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+    field public static final int TETHERING_USB = 1; // 0x1
+    field public static final int TETHERING_WIFI = 0; // 0x0
+    field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+    field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+    field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TYPE_NONE = -1; // 0xffffffff
+    field @Deprecated public static final int TYPE_PROXY = 16; // 0x10
+    field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd
+  }
+
+  public static interface ConnectivityManager.OnSetOemNetworkPreferenceListener {
+    method public void onComplete();
+  }
+
+  @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback {
+    ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback();
+    method @Deprecated public void onTetheringFailed();
+    method @Deprecated public void onTetheringStarted();
+  }
+
+  @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener {
+    method @Deprecated public void onTetheringEntitlementResult(int);
+  }
+
+  @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback {
+    ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback();
+    method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  public final class InvalidPacketException extends java.lang.Exception {
+    ctor public InvalidPacketException(int);
+    method public int getError();
+    field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
+    field public static final int ERROR_INVALID_LENGTH = -23; // 0xffffffe9
+    field public static final int ERROR_INVALID_PORT = -22; // 0xffffffea
+  }
+
+  public final class IpConfiguration implements android.os.Parcelable {
+    ctor public IpConfiguration();
+    ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
+    method public int describeContents();
+    method @Nullable public android.net.ProxyInfo getHttpProxy();
+    method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
+    method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
+    method @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
+    method public void setHttpProxy(@Nullable android.net.ProxyInfo);
+    method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
+    method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
+    method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
+  }
+
+  public enum IpConfiguration.IpAssignment {
+    enum_constant public static final android.net.IpConfiguration.IpAssignment DHCP;
+    enum_constant public static final android.net.IpConfiguration.IpAssignment STATIC;
+    enum_constant public static final android.net.IpConfiguration.IpAssignment UNASSIGNED;
+  }
+
+  public enum IpConfiguration.ProxySettings {
+    enum_constant public static final android.net.IpConfiguration.ProxySettings NONE;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings PAC;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings STATIC;
+    enum_constant public static final android.net.IpConfiguration.ProxySettings UNASSIGNED;
+  }
+
+  public final class IpPrefix implements android.os.Parcelable {
+    ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+    ctor public IpPrefix(@NonNull String);
+  }
+
+  public class KeepalivePacketData {
+    ctor protected KeepalivePacketData(@NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull java.net.InetAddress, @IntRange(from=0, to=65535) int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method @NonNull public java.net.InetAddress getDstAddress();
+    method public int getDstPort();
+    method @NonNull public byte[] getPacket();
+    method @NonNull public java.net.InetAddress getSrcAddress();
+    method public int getSrcPort();
+  }
+
+  public class LinkAddress implements android.os.Parcelable {
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int);
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long);
+    ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int);
+    ctor public LinkAddress(@NonNull String);
+    ctor public LinkAddress(@NonNull String, int, int);
+    method public long getDeprecationTime();
+    method public long getExpirationTime();
+    method public boolean isGlobalPreferred();
+    method public boolean isIpv4();
+    method public boolean isIpv6();
+    method public boolean isSameAddressAs(@Nullable android.net.LinkAddress);
+    field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+  }
+
+  public final class LinkProperties implements android.os.Parcelable {
+    ctor public LinkProperties(@Nullable android.net.LinkProperties);
+    ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean);
+    method public boolean addDnsServer(@NonNull java.net.InetAddress);
+    method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
+    method public boolean addPcscfServer(@NonNull java.net.InetAddress);
+    method @NonNull public java.util.List<java.net.InetAddress> getAddresses();
+    method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
+    method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
+    method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
+    method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+    method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
+    method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
+    method @Nullable public String getTcpBufferSizes();
+    method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
+    method public boolean hasGlobalIpv6Address();
+    method public boolean hasIpv4Address();
+    method public boolean hasIpv4DefaultRoute();
+    method public boolean hasIpv4DnsServer();
+    method public boolean hasIpv6DefaultRoute();
+    method public boolean hasIpv6DnsServer();
+    method public boolean isIpv4Provisioned();
+    method public boolean isIpv6Provisioned();
+    method public boolean isProvisioned();
+    method public boolean isReachable(@NonNull java.net.InetAddress);
+    method public boolean removeDnsServer(@NonNull java.net.InetAddress);
+    method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
+    method public boolean removeRoute(@NonNull android.net.RouteInfo);
+    method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+    method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
+    method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
+    method public void setPrivateDnsServerName(@Nullable String);
+    method public void setTcpBufferSizes(@Nullable String);
+    method public void setUsePrivateDns(boolean);
+    method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
+  }
+
+  public final class NattKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+    ctor public NattKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[]) throws android.net.InvalidPacketException;
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NattKeepalivePacketData> CREATOR;
+  }
+
+  public class Network implements android.os.Parcelable {
+    ctor public Network(@NonNull android.net.Network);
+    method public int getNetId();
+    method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
+  }
+
+  public abstract class NetworkAgent {
+    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+    method @Nullable public android.net.Network getNetwork();
+    method public void markConnected();
+    method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
+    method public void onAutomaticReconnectDisabled();
+    method public void onNetworkUnwanted();
+    method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
+    method public void onQosCallbackUnregistered(int);
+    method public void onRemoveKeepalivePacketFilter(int);
+    method public void onSaveAcceptUnvalidated(boolean);
+    method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
+    method public void onStartSocketKeepalive(int, @NonNull java.time.Duration, @NonNull android.net.KeepalivePacketData);
+    method public void onStopSocketKeepalive(int);
+    method public void onValidationStatus(int, @Nullable android.net.Uri);
+    method @NonNull public android.net.Network register();
+    method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
+    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+    method public final void sendQosCallbackError(int, int);
+    method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
+    method public final void sendQosSessionLost(int, int);
+    method public final void sendSocketKeepaliveEvent(int, int);
+    method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
+    method public void unregister();
+    field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
+    field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
+  }
+
+  public final class NetworkAgentConfig implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLegacyType();
+    method @NonNull public String getLegacyTypeName();
+    method public boolean isExplicitlySelected();
+    method public boolean isPartialConnectivityAcceptable();
+    method public boolean isUnvalidatedConnectivityAcceptable();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+  }
+
+  public static final class NetworkAgentConfig.Builder {
+    ctor public NetworkAgentConfig.Builder();
+    method @NonNull public android.net.NetworkAgentConfig build();
+    method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setPartialConnectivityAcceptable(boolean);
+    method @NonNull public android.net.NetworkAgentConfig.Builder setUnvalidatedConnectivityAcceptable(boolean);
+  }
+
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    ctor public NetworkCapabilities(@Nullable android.net.NetworkCapabilities, boolean);
+    method @NonNull public int[] getAdministratorUids();
+    method @Nullable public String getSsid();
+    method @NonNull public int[] getTransportTypes();
+    method public boolean isPrivateDnsBroken();
+    method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+    field public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; // 0x1c
+    field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+    field public static final int NET_CAPABILITY_OEM_PRIVATE = 26; // 0x1a
+    field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
+    field public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27; // 0x1b
+  }
+
+  public static final class NetworkCapabilities.Builder {
+    ctor public NetworkCapabilities.Builder();
+    ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
+    method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int);
+    method @NonNull public android.net.NetworkCapabilities build();
+    method @NonNull public android.net.NetworkCapabilities.Builder clearAll();
+    method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int);
+    method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+  }
+
+  public class NetworkProvider {
+    ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest);
+    method public int getProviderId();
+    method public void onNetworkRequestWithdrawn(@NonNull android.net.NetworkRequest);
+    method public void onNetworkRequested(@NonNull android.net.NetworkRequest, @IntRange(from=0, to=99) int, int);
+    field public static final int ID_NONE = -1; // 0xffffffff
+  }
+
+  public class NetworkRequest implements android.os.Parcelable {
+    method @Nullable public String getRequestorPackageName();
+    method public int getRequestorUid();
+  }
+
+  public static class NetworkRequest.Builder {
+    method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
+  }
+
+  public final class RouteInfo implements android.os.Parcelable {
+    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
+    ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
+    method public int getMtu();
+    method public int getType();
+    field public static final int RTN_THROW = 9; // 0x9
+    field public static final int RTN_UNICAST = 1; // 0x1
+    field public static final int RTN_UNREACHABLE = 7; // 0x7
+  }
+
+  public abstract class SocketKeepalive implements java.lang.AutoCloseable {
+    field public static final int SUCCESS = 0; // 0x0
+  }
+
+  public final class StaticIpConfiguration implements android.os.Parcelable {
+    ctor public StaticIpConfiguration();
+    ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+    method public void addDnsServer(@NonNull java.net.InetAddress);
+    method public void clear();
+    method public int describeContents();
+    method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
+    method @Nullable public String getDomains();
+    method @Nullable public java.net.InetAddress getGateway();
+    method @Nullable public android.net.LinkAddress getIpAddress();
+    method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR;
+  }
+
+  public static final class StaticIpConfiguration.Builder {
+    ctor public StaticIpConfiguration.Builder();
+    method @NonNull public android.net.StaticIpConfiguration build();
+    method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress);
+    method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
+  }
+
+  public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
+    ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
+    field public final int ipTos;
+    field public final int ipTtl;
+    field public final int tcpAck;
+    field public final int tcpSeq;
+    field public final int tcpWindow;
+    field public final int tcpWindowScale;
+  }
+
+  public interface TransportInfo {
+    method public default boolean hasLocationSensitiveFields();
+    method @NonNull public default android.net.TransportInfo makeCopy(boolean);
+  }
+
+}
+
+package android.net.apf {
+
+  public final class ApfCapabilities implements android.os.Parcelable {
+    ctor public ApfCapabilities(int, int, int);
+    method public int describeContents();
+    method public static boolean getApfDrop8023Frames();
+    method @NonNull public static int[] getApfEtherTypeBlackList();
+    method public boolean hasDataAccess();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR;
+    field public final int apfPacketFormat;
+    field public final int apfVersionSupported;
+    field public final int maximumApfProgramSize;
+  }
+
+}
+
+package android.net.util {
+
+  public final class SocketUtils {
+    method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
+    method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
+    method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
+    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
+    method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
+    method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]);
+  }
+
+}
+
diff --git a/packages/Connectivity/framework/api/system-lint-baseline.txt b/packages/Connectivity/framework/api/system-lint-baseline.txt
new file mode 100644
index 0000000..9a97707
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-lint-baseline.txt
@@ -0,0 +1 @@
+// Baseline format: 1.0
diff --git a/packages/Connectivity/framework/api/system-removed.txt b/packages/Connectivity/framework/api/system-removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/packages/Connectivity/framework/api/system-removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 4e3085f..b4a651c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
 
 package android.net;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@
 import android.text.TextUtils;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -154,8 +172,30 @@
      * needed in terms of {@link NetworkCapabilities} features
      */
     public static class Builder {
+        /**
+         * Capabilities that are currently compatible with VCN networks.
+         */
+        private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+                NET_CAPABILITY_CAPTIVE_PORTAL,
+                NET_CAPABILITY_DUN,
+                NET_CAPABILITY_FOREGROUND,
+                NET_CAPABILITY_INTERNET,
+                NET_CAPABILITY_NOT_CONGESTED,
+                NET_CAPABILITY_NOT_METERED,
+                NET_CAPABILITY_NOT_RESTRICTED,
+                NET_CAPABILITY_NOT_ROAMING,
+                NET_CAPABILITY_NOT_SUSPENDED,
+                NET_CAPABILITY_NOT_VPN,
+                NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+                NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_VALIDATED);
+
         private final NetworkCapabilities mNetworkCapabilities;
 
+        // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+        private boolean mModifiedNotVcnManaged = false;
+
         /**
          * Default constructor for Builder.
          */
@@ -177,6 +217,7 @@
             // maybeMarkCapabilitiesRestricted() doesn't add back.
             final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
             nc.maybeMarkCapabilitiesRestricted();
+            deduceNotVcnManagedCapability(nc);
             return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
                     ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
         }
@@ -193,6 +234,9 @@
          */
         public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -204,6 +248,9 @@
          */
         public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.removeCapability(capability);
+            if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+                mModifiedNotVcnManaged = true;
+            }
             return this;
         }
 
@@ -261,6 +308,9 @@
         @NonNull
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
+            // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+            // should not be add back later.
+            mModifiedNotVcnManaged = true;
             return this;
         }
 
@@ -380,6 +430,25 @@
             mNetworkCapabilities.setSignalStrength(signalStrength);
             return this;
         }
+
+        /**
+         * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+         * and user intention, which includes:
+         *   1. For the requests that don't have anything besides
+         *      {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
+         *      allow the callers automatically utilize VCN networks if available.
+         *   2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+         *      do not alter them to allow user fire request that suits their need.
+         *
+         * @hide
+         */
+        private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+            if (mModifiedNotVcnManaged) return;
+            for (final int cap : nc.getCapabilities()) {
+                if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+            }
+            nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+        }
     }
 
     // implement the Parcelable interface
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 85e3fa3..43fffd7 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -40,6 +40,8 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 
 /**
  * A class to encapsulate management of the "Smart Networking" capability of
@@ -73,6 +75,32 @@
     private volatile int mMeteredMultipathPreference;
     private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+    // Mainline module can't use internal HandlerExecutor, so add an identical executor here.
+    private static class HandlerExecutor implements Executor {
+        @NonNull
+        private final Handler mHandler;
+
+        HandlerExecutor(@NonNull Handler handler) {
+            mHandler = handler;
+        }
+        @Override
+        public void execute(Runnable command) {
+            if (!mHandler.post(command)) {
+                throw new RejectedExecutionException(mHandler + " is shutting down");
+            }
+        }
+    }
+
+    @VisibleForTesting
+    protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener
+            implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            mActiveSubId = subId;
+            reevaluateInternal();
+        }
+    }
+
     public MultinetworkPolicyTracker(Context ctx, Handler handler) {
         this(ctx, handler, null);
     }
@@ -93,14 +121,8 @@
             }
         };
 
-        ctx.getSystemService(TelephonyManager.class).listen(
-                new PhoneStateListener(handler.getLooper()) {
-            @Override
-            public void onActiveDataSubscriptionIdChanged(int subId) {
-                mActiveSubId = subId;
-                reevaluateInternal();
-            }
-        }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+        ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener(
+                new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener());
 
         updateAvoidBadWifi();
         updateMeteredMultipathPreference();
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index f67e039..71e0910 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -84,8 +84,6 @@
     <uses-permission android:name="android.permission.READ_INPUT_STATE" />
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-    <!--  TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
-    <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
     <uses-permission android:name="com.android.permission.USE_SYSTEM_DATA_LOADERS" />
     <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.KEEP_UNINSTALLED_PACKAGES" />
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index bcd28a6..53f7e44 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,6 +50,4 @@
     public abstract void onStateChanged(State state);
 
     public abstract int getDetailY();
-
-    public void setShowLabels(boolean show) {}
 }
diff --git a/packages/SystemUI/res/drawable/control_no_favorites_background.xml b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
index d895dd0..2165b12 100644
--- a/packages/SystemUI/res/drawable/control_no_favorites_background.xml
+++ b/packages/SystemUI/res/drawable/control_no_favorites_background.xml
@@ -26,12 +26,4 @@
             <corners android:radius="@dimen/control_corner_radius" />
         </shape>
     </item>
-    <item>
-        <shape>
-            <stroke
-                android:width="1dp"
-                android:color="#4DFFFFFF" />
-            <corners android:radius="@dimen/control_corner_radius"/>
-        </shape>
-    </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
index cb4686dd..1ccb176 100644
--- a/packages/SystemUI/res/drawable/controls_dialog_bg.xml
+++ b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <solid android:color="?android:attr/colorBackground" />
     <corners android:radius="@dimen/notification_corner_radius" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/horizontal_ellipsis.xml b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml
new file mode 100644
index 0000000..1800857
--- /dev/null
+++ b/packages/SystemUI/res/drawable/horizontal_ellipsis.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0"
+    android:tint="?android:attr/colorBackgroundFloating" >
+
+    <path
+        android:pathData="M6 10c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm12 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm-6 0c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2z"
+        android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_tile_label_divider.xml b/packages/SystemUI/res/drawable/volume_drawer_bg.xml
similarity index 61%
rename from packages/SystemUI/res/layout/qs_tile_label_divider.xml
rename to packages/SystemUI/res/drawable/volume_drawer_bg.xml
index 150a5b8..f0e2292 100644
--- a/packages/SystemUI/res/layout/qs_tile_label_divider.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_bg.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Android Open Source Project
+  ~ Copyright (C) 2021 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -14,5 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<View />
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <size android:width="@dimen/volume_ringer_drawer_item_size" />
+    <solid android:color="?android:attr/colorBackgroundFloating" />
+    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
new file mode 100644
index 0000000..5e7cb12
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <size
+        android:height="@dimen/volume_ringer_drawer_item_size"
+        android:width="@dimen/volume_ringer_drawer_item_size" />
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar.xml b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
new file mode 100644
index 0000000..b0e0ed5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- SeekBar drawable for volume rows. This contains a background layer (with a solid round rect,
+     and a bottom-aligned icon) and a progress layer (with an accent-colored round rect and icon)
+     that moves up and down with the progress value. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
+    <item android:id="@android:id/background"
+        android:gravity="center_vertical|fill_horizontal">
+        <layer-list>
+            <item android:id="@+id/volume_seekbar_background_solid">
+                <shape>
+                    <size android:height="@dimen/volume_dialog_panel_width" />
+                    <solid android:color="?android:attr/colorBackgroundFloating" />
+                    <corners android:radius="@dimen/volume_dialog_panel_width_half" />
+                </shape>
+            </item>
+            <item
+                android:id="@+id/volume_seekbar_background_icon"
+                android:gravity="center_vertical|left"
+                android:height="@dimen/rounded_slider_icon_size"
+                android:width="@dimen/rounded_slider_icon_size"
+                android:left="@dimen/rounded_slider_icon_inset">
+                <rotate
+                    android:fromDegrees="-270"
+                    android:toDegrees="-270">
+                    <!-- A placeholder drawable is required here - it'll be replaced in code. -->
+                    <com.android.systemui.util.AlphaTintDrawableWrapper
+                        android:drawable="@drawable/ic_volume_media"
+                        android:tint="?android:attr/colorAccent" />
+                </rotate>
+            </item>
+        </layer-list>
+    </item>
+    <item android:id="@android:id/progress"
+          android:gravity="center_vertical|fill_horizontal">
+            <com.android.systemui.util.RoundedCornerProgressDrawable
+                android:drawable="@drawable/volume_row_seekbar_progress"
+            />
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
new file mode 100644
index 0000000..ef20236
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_row_seekbar_progress.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Progress drawable for volume row SeekBars. This is the accent-colored round rect that moves up
+     and down as the progress value changes. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="true">
+    <item android:id="@+id/volume_seekbar_progress_solid">
+        <shape>
+            <size android:height="@dimen/volume_dialog_panel_width" />
+            <solid android:color="?android:attr/colorAccent" />
+            <corners android:radius="@dimen/volume_dialog_panel_width_half"/>
+        </shape>
+    </item>
+    <item
+        android:id="@+id/volume_seekbar_progress_icon"
+        android:gravity="center_vertical|right"
+        android:height="@dimen/rounded_slider_icon_size"
+        android:width="@dimen/rounded_slider_icon_size"
+        android:right="@dimen/rounded_slider_icon_inset">
+        <rotate
+            android:fromDegrees="-270"
+            android:toDegrees="-270">
+            <!-- A placeholder drawable is required here - it'll be replaced in code. -->
+            <com.android.systemui.util.AlphaTintDrawableWrapper
+                android:drawable="@drawable/ic_volume_media"
+                android:tint="?android:attr/colorBackgroundFloating" />
+        </rotate>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index c420117..237dc02 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -32,105 +32,124 @@
         android:gravity="right"
         android:layout_gravity="right"
         android:background="@android:color/transparent"
-        android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
-        android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
+        android:paddingRight="@dimen/volume_dialog_stream_padding"
         android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
         android:clipToPadding="false">
 
-        <FrameLayout
-            android:id="@+id/ringer"
-            android:layout_width="@dimen/volume_dialog_ringer_size"
-            android:layout_height="@dimen/volume_dialog_ringer_size"
-            android:layout_marginBottom="@dimen/volume_dialog_spacer"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full">
-            <com.android.keyguard.AlphaOptimizedImageButton
-                android:id="@+id/ringer_icon"
-                style="@style/VolumeButtons"
-                android:background="@drawable/rounded_ripple"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:scaleType="fitCenter"
-                android:padding="@dimen/volume_dialog_ringer_icon_padding"
-                android:tint="@color/accent_tint_color_selector"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false" />
-
-            <include layout="@layout/volume_dnd_icon"
-                     android:layout_width="match_parent"
-                     android:layout_height="wrap_content"
-                     android:layout_marginRight="@dimen/volume_dialog_stream_padding"
-                     android:layout_marginTop="6dp"/>
-        </FrameLayout>
-
+        <!--
+            Container for a) the ringer drawer and the caption button next to b) the volume rows.
+        -->
         <LinearLayout
-            android:id="@+id/main"
             android:layout_width="wrap_content"
-            android:minWidth="@dimen/volume_dialog_panel_width"
             android:layout_height="wrap_content"
-            android:layout_marginTop="68dp"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:orientation="vertical"
-            android:translationZ="@dimen/volume_dialog_elevation"
+            android:orientation="horizontal"
             android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full" >
-            <LinearLayout
-                android:id="@+id/volume_dialog_rows"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:minWidth="@dimen/volume_dialog_panel_width"
-                android:gravity="center"
-                android:orientation="horizontal"
-                android:paddingRight="@dimen/volume_dialog_stream_padding"
-                android:paddingLeft="@dimen/volume_dialog_stream_padding">
-                <!-- volume rows added and removed here! :-) -->
-            </LinearLayout>
-            <FrameLayout
-                android:id="@+id/settings_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@drawable/rounded_bg_bottom_background">
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/settings"
-                    android:src="@drawable/ic_tune_black_16dp"
-                    android:layout_width="@dimen/volume_dialog_tap_target_size"
-                    android:layout_height="@dimen/volume_dialog_tap_target_size"
-                    android:layout_gravity="center"
-                    android:contentDescription="@string/accessibility_volume_settings"
-                    android:background="@drawable/ripple_drawable_20dp"
-                    android:tint="?android:attr/textColorSecondary"
-                    android:soundEffectsEnabled="false" />
-            </FrameLayout>
-        </LinearLayout>
+            android:clipToPadding="false">
 
-        <FrameLayout
-            android:id="@+id/odi_captions"
-            android:layout_width="@dimen/volume_dialog_caption_size"
-            android:layout_height="@dimen/volume_dialog_caption_size"
-            android:layout_marginRight="68dp"
-            android:gravity="right"
-            android:layout_gravity="right"
-            android:clipToPadding="false"
-            android:translationZ="@dimen/volume_dialog_elevation"
-            android:background="@drawable/rounded_bg_full">
-            <com.android.systemui.volume.CaptionsToggleImageButton
-                android:id="@+id/odi_captions_icon"
-                android:src="@drawable/ic_volume_odi_captions_disabled"
-                style="@style/VolumeButtons"
-                android:background="@drawable/rounded_ripple"
-                android:layout_width="match_parent"
+            <!-- The ringer drawer and the caption button. -->
+            <FrameLayout
+                android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:tint="@color/caption_tint_color_selector"
-                android:layout_gravity="center"
-                android:soundEffectsEnabled="false"
-                sysui:optedOut="false"/>
-        </FrameLayout>
+                android:paddingRight="@dimen/volume_dialog_stream_padding"
+                android:clipChildren="false"
+                android:clipToPadding="false"
+                android:orientation="vertical">
+
+                <include layout="@layout/volume_ringer_drawer"
+                    android:layout_gravity="top|right"/>
+
+                <FrameLayout
+                    android:id="@+id/odi_captions"
+                    android:layout_width="@dimen/volume_dialog_caption_size"
+                    android:layout_height="@dimen/volume_dialog_caption_size"
+                    android:gravity="center"
+                    android:layout_gravity="bottom|right"
+                    android:layout_marginBottom="@dimen/volume_dialog_tap_target_size"
+                    android:clipToPadding="false">
+
+                    <com.android.systemui.volume.CaptionsToggleImageButton
+                        android:id="@+id/odi_captions_icon"
+                        android:src="@drawable/ic_volume_odi_captions_disabled"
+                        style="@style/VolumeButtons"
+                        android:background="@drawable/rounded_ripple"
+                        android:layout_width="match_parent"
+                        android:layout_height="match_parent"
+                        android:tint="@color/caption_tint_color_selector"
+                        android:layout_gravity="center"
+                        android:soundEffectsEnabled="false"
+                        sysui:optedOut="false"/>
+
+                </FrameLayout>
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:visibility="gone"
+                android:id="@+id/ringer"
+                android:layout_width="@dimen/volume_dialog_ringer_size"
+                android:layout_height="@dimen/volume_dialog_ringer_size"
+                android:layout_marginBottom="@dimen/volume_dialog_spacer"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:translationZ="@dimen/volume_dialog_elevation"
+                android:clipToPadding="false"
+                android:background="@drawable/rounded_bg_full">
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/ringer_icon"
+                    style="@style/VolumeButtons"
+                    android:background="@drawable/rounded_ripple"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:scaleType="fitCenter"
+                    android:padding="@dimen/volume_dialog_ringer_icon_padding"
+                    android:tint="@color/accent_tint_color_selector"
+                    android:layout_gravity="center"
+                    android:soundEffectsEnabled="false" />
+
+                <include layout="@layout/volume_dnd_icon"
+                         android:layout_width="match_parent"
+                         android:layout_height="wrap_content"
+                         android:layout_marginRight="@dimen/volume_dialog_stream_padding"
+                         android:layout_marginTop="6dp"/>
+            </FrameLayout>
+
+            <LinearLayout
+                android:id="@+id/main"
+                android:layout_width="wrap_content"
+                android:minWidth="@dimen/volume_dialog_panel_width"
+                android:layout_height="wrap_content"
+                android:gravity="right"
+                android:layout_gravity="right"
+                android:orientation="vertical"
+                android:clipChildren="false"
+                android:clipToPadding="false">
+                <LinearLayout
+                    android:id="@+id/volume_dialog_rows"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:minWidth="@dimen/volume_dialog_panel_width"
+                    android:gravity="center"
+                    android:orientation="horizontal">
+                    <!-- volume rows added and removed here! :-) -->
+                </LinearLayout>
+                <FrameLayout
+                    android:id="@+id/settings_container"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <com.android.keyguard.AlphaOptimizedImageButton
+                        android:id="@+id/settings"
+                        android:src="@drawable/horizontal_ellipsis"
+                        android:layout_width="@dimen/volume_dialog_tap_target_size"
+                        android:layout_height="@dimen/volume_dialog_tap_target_size"
+                        android:layout_gravity="center"
+                        android:contentDescription="@string/accessibility_volume_settings"
+                        android:background="@drawable/ripple_drawable_20dp"
+                        android:tint="?android:attr/colorBackgroundFloating"
+                        android:soundEffectsEnabled="false" />
+                </FrameLayout>
+            </LinearLayout>
+
+        </LinearLayout>
 
         <ViewStub
             android:id="@+id/odi_captions_tooltip_stub"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 0822947..93dd1a1 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -81,6 +81,22 @@
             android:layout_height="match_parent"
             android:layout_weight="@integer/qs_footer_actions_weight"
             android:gravity="center_vertical|end" >
+
+            <com.android.systemui.statusbar.AlphaOptimizedImageView
+                android:id="@+id/pm_lite"
+                android:layout_width="@dimen/qs_footer_action_button_size"
+                android:layout_height="@dimen/qs_footer_action_button_size"
+                android:background="?android:attr/selectableItemBackgroundBorderless"
+                android:clickable="true"
+                android:clipToPadding="false"
+                android:contentDescription="@string/accessibility_quick_settings_edit"
+                android:focusable="true"
+                android:padding="@dimen/qs_footer_icon_padding"
+                android:src="@*android:drawable/ic_lock_power_off"
+                android:tint="?android:attr/colorForeground"
+                android:visibility="gone"
+                />
+
             <com.android.systemui.statusbar.phone.MultiUserSwitch
                 android:id="@+id/multi_user_switch"
                 android:layout_width="@dimen/qs_footer_action_button_size"
diff --git a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
index ee54f1d..c830773 100644
--- a/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
+++ b/packages/SystemUI/res/layout/qs_paged_page_side_labels.xml
@@ -14,4 +14,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<include layout="@layout/qs_paged_page" />
+<com.android.systemui.qs.SideLabelTileLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tile_page"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4ca59f5..bbb6107 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -55,12 +55,14 @@
             android:id="@+id/qs_frame"
             android:layout="@layout/qs_panel"
             android:layout_width="@dimen/qs_panel_width"
-            android:layout_height="match_parent"
+            android:layout_height="0dp"
             android:clipToPadding="false"
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS"
             systemui:layout_constraintStart_toStartOf="parent"
             systemui:layout_constraintEnd_toEndOf="parent"
+            systemui:layout_constraintTop_toTopOf="parent"
+            systemui:layout_constraintBottom_toBottomOf="parent"
         />
 
         <androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 1810c19..6aac5a3 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -21,6 +21,8 @@
     android:layout_height="wrap_content"
     android:gravity="right"
     android:layout_gravity="right"
+    android:paddingRight="@dimen/volume_dialog_stream_padding"
+    android:clipToPadding="false"
     android:background="@android:color/transparent"
     android:theme="@style/volume_dialog_theme">
 
@@ -34,13 +36,15 @@
         android:layout_gravity="right"
         android:background="@android:color/transparent"
         android:paddingRight="@dimen/volume_dialog_panel_transparent_padding_right"
-        android:paddingTop="@dimen/volume_dialog_panel_transparent_padding"
-        android:paddingBottom="@dimen/volume_dialog_panel_transparent_padding"
         android:paddingLeft="@dimen/volume_dialog_panel_transparent_padding"
         android:orientation="vertical"
-        android:clipToPadding="false">
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <include layout="@layout/volume_ringer_drawer" />
 
         <FrameLayout
+            android:visibility="gone"
             android:id="@+id/ringer"
             android:layout_width="@dimen/volume_dialog_ringer_size"
             android:layout_height="@dimen/volume_dialog_ringer_size"
@@ -77,10 +81,8 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:orientation="vertical"
-            android:translationZ="@dimen/volume_dialog_elevation"
             android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full" >
+            android:clipToPadding="false" >
             <LinearLayout
                 android:id="@+id/volume_dialog_rows"
                 android:layout_width="wrap_content"
@@ -88,24 +90,22 @@
                 android:minWidth="@dimen/volume_dialog_panel_width"
                 android:gravity="center"
                 android:orientation="horizontal"
-                android:paddingRight="@dimen/volume_dialog_stream_padding"
-                android:paddingLeft="@dimen/volume_dialog_stream_padding">
+                android:layout_marginTop="@dimen/volume_row_slider_padding_start">
                     <!-- volume rows added and removed here! :-) -->
             </LinearLayout>
             <FrameLayout
                 android:id="@+id/settings_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@drawable/rounded_bg_bottom_background">
+                android:layout_height="wrap_content">
                 <com.android.keyguard.AlphaOptimizedImageButton
                     android:id="@+id/settings"
-                    android:src="@drawable/ic_tune_black_16dp"
+                    android:src="@drawable/horizontal_ellipsis"
                     android:layout_width="@dimen/volume_dialog_tap_target_size"
                     android:layout_height="@dimen/volume_dialog_tap_target_size"
                     android:layout_gravity="center"
                     android:contentDescription="@string/accessibility_volume_settings"
                     android:background="@drawable/ripple_drawable_20dp"
-                    android:tint="?android:attr/textColorPrimary"
+                    android:tint="?android:attr/colorBackgroundFloating"
                     android:soundEffectsEnabled="false" />
             </FrameLayout>
         </LinearLayout>
@@ -118,7 +118,6 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:clipToPadding="false"
-            android:translationZ="@dimen/volume_dialog_elevation"
             android:background="@drawable/rounded_bg_full">
             <com.android.systemui.volume.CaptionsToggleImageButton
                 android:id="@+id/odi_captions_icon"
@@ -127,7 +126,7 @@
                 android:background="@drawable/rounded_ripple"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:tint="?android:attr/textColorPrimary"
+                android:tint="?android:attr/colorAccent"
                 android:layout_gravity="center"
                 android:soundEffectsEnabled="false"
                 sysui:optedOut="false"/>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index b9efc5b..fda59b5 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -20,11 +20,12 @@
     android:layout_width="@dimen/volume_dialog_panel_width"
     android:clipChildren="false"
     android:clipToPadding="false"
+    android:translationZ="@dimen/volume_dialog_elevation"
     android:theme="@style/volume_dialog_theme">
 
     <LinearLayout
         android:layout_height="wrap_content"
-        android:layout_width="match_parent"
+        android:layout_width="@dimen/volume_dialog_panel_width"
         android:gravity="center"
         android:layout_gravity="center"
         android:orientation="vertical" >
@@ -41,21 +42,23 @@
         <FrameLayout
             android:id="@+id/volume_row_slider_frame"
             android:layout_width="match_parent"
-            android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
-            android:layout_marginBottom="@dimen/volume_dialog_slider_margin_bottom"
-            android:layoutDirection="rtl"
-            android:layout_height="@dimen/volume_dialog_slider_height">
+            android:layout_height="@dimen/volume_row_slider_height">
+            <include layout="@layout/volume_dnd_icon"/>
             <SeekBar
                 android:id="@+id/volume_row_slider"
+                android:paddingLeft="0dp"
+                android:paddingRight="0dp"
+                android:paddingStart="0dp"
+                android:paddingEnd="0dp"
                 android:clickable="true"
-                android:layout_width="@dimen/volume_dialog_slider_height"
+                android:layout_width="@dimen/volume_row_slider_height"
                 android:layout_height="match_parent"
-                android:layoutDirection="rtl"
                 android:layout_gravity="center"
-                android:rotation="90" />
+                android:rotation="270" />
         </FrameLayout>
 
         <com.android.keyguard.AlphaOptimizedImageButton
+            android:visibility="gone"
             android:id="@+id/volume_row_icon"
             style="@style/VolumeButtons"
             android:layout_width="@dimen/volume_dialog_tap_target_size"
@@ -66,6 +69,4 @@
             android:soundEffectsEnabled="false" />
     </LinearLayout>
 
-    <include layout="@layout/volume_dnd_icon"/>
-
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
new file mode 100644
index 0000000..d6e1782
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:clipToPadding="false"
+    android:clipChildren="false">
+
+    <!-- Drawer view, invisible by default. -->
+    <FrameLayout
+        android:id="@+id/volume_drawer_container"
+        android:alpha="0.0"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/volume_drawer_bg"
+        android:orientation="vertical">
+
+        <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
+        <FrameLayout
+            android:id="@+id/volume_drawer_selection_background"
+            android:alpha="0.0"
+            android:layout_width="@dimen/volume_ringer_drawer_item_size"
+            android:layout_height="@dimen/volume_ringer_drawer_item_size"
+            android:layout_gravity="bottom|right"
+            android:background="@drawable/volume_drawer_selection_bg" />
+
+        <LinearLayout
+            android:id="@+id/volume_drawer_options"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_vibrate"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_vibrate"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_vibrate_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer_vibrate" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_mute"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_mute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_mute_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer_mute" />
+
+            </FrameLayout>
+
+            <FrameLayout
+                android:id="@+id/volume_drawer_normal"
+                android:layout_width="@dimen/volume_ringer_drawer_item_size"
+                android:layout_height="@dimen/volume_ringer_drawer_item_size"
+                android:description="@string/volume_ringer_hint_unmute"
+                android:gravity="center">
+
+                <ImageView
+                    android:id="@+id/volume_drawer_normal_icon"
+                    android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+                    android:layout_gravity="center"
+                    android:tint="?android:attr/colorAccent"
+                    android:src="@drawable/ic_volume_ringer" />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+    <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
+         position in the drawer. When the drawer is closed, it animates back. -->
+    <FrameLayout
+        android:id="@+id/volume_new_ringer_active_icon_container"
+        android:layout_width="@dimen/volume_ringer_drawer_item_size"
+        android:layout_height="@dimen/volume_ringer_drawer_item_size"
+        android:layout_gravity="bottom|right"
+        android:description="@string/volume_ringer_change"
+        android:background="@drawable/volume_drawer_selection_bg">
+
+        <ImageView
+            android:id="@+id/volume_new_ringer_active_icon"
+            android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+            android:layout_gravity="center"
+            android:tint="?android:attr/colorBackgroundFloating"
+            android:src="@drawable/ic_volume_media" />
+
+    </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index acd671c..3bc1c80 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -265,7 +265,6 @@
     <color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
     <color name="control_thumbnail_tint">#33000000</color>
     <color name="control_thumbnail_shadow_color">@*android:color/black</color>
-    <color name="controls_lockscreen_scrim">#AA000000</color>
 
     <!-- Docked misalignment message -->
     <color name="misalignment_text_color">#F28B82</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 89c849a..ea0ea5e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -456,10 +456,12 @@
 
     <dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
 
-    <dimen name="volume_dialog_stream_padding">8dp</dimen>
+    <dimen name="volume_dialog_stream_padding">12dp</dimen>
 
     <dimen name="volume_dialog_panel_width">64dp</dimen>
 
+    <dimen name="volume_dialog_panel_width_half">32dp</dimen>
+
     <dimen name="volume_dialog_slider_height">116dp</dimen>
 
     <dimen name="volume_dialog_ringer_size">64dp</dimen>
@@ -486,6 +488,13 @@
 
     <dimen name="volume_tool_tip_arrow_corner_radius">2dp</dimen>
 
+    <!-- Size of each item in the ringer selector drawer. -->
+    <dimen name="volume_ringer_drawer_item_size">64dp</dimen>
+    <dimen name="volume_ringer_drawer_item_size_half">32dp</dimen>
+
+    <!-- Size of the icon inside each item in the ringer selector drawer. -->
+    <dimen name="volume_ringer_drawer_icon_size">24dp</dimen>
+
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
 
@@ -966,7 +975,7 @@
     <dimen name="volume_row_padding_start">4dp</dimen>
     <dimen name="volume_row_header_padding_start">16dp</dimen>
     <dimen name="volume_row_height">64dp</dimen>
-    <dimen name="volume_row_slider_height">48dp</dimen>
+    <dimen name="volume_row_slider_height">192dp</dimen>
     <dimen name="volume_row_slider_padding_start">12dp</dimen>
 
     <dimen name="volume_expander_margin_end">2dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 3e16cd4..e551892 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -44,4 +44,6 @@
     <bool name="flag_toast_style">false</bool>
 
     <bool name="flag_navigation_bar_overlay">false</bool>
+
+    <bool name="flag_pm_lite">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4b95c16..5f8df5a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -959,7 +959,7 @@
     <!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
     <string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
     <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string>
+    <string name="quick_settings_reduce_bright_colors_label">Reduce brightness</string>
 
     <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
     <string name="quick_settings_nfc_label">NFC</string>
@@ -1054,6 +1054,9 @@
     <!-- Text on keyguard screen and in Quick Settings footer indicating that user's device belongs to their organization. [CHAR LIMIT=40] -->
     <string name="do_disclosure_with_name">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
 
+    <!-- Text on keyguard screen and in Quick Settings footer indicating that the user's device is provided by the Creditor. [CHAR LIMIT=60] -->
+    <string name="do_financed_disclosure_with_name">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
+
     <!-- Shows when people have clicked on the phone icon [CHAR LIMIT=60] -->
     <string name="phone_hint">Swipe from icon for phone</string>
 
@@ -1558,6 +1561,8 @@
     <string name="volume_stream_content_description_vibrate_a11y">%1$s. Tap to set to vibrate.</string>
     <string name="volume_stream_content_description_mute_a11y">%1$s. Tap to mute.</string>
 
+    <string name="volume_ringer_change">Tap to change ringer mode</string>
+
     <!-- Hint for accessibility. For example: double tap to mute [CHAR_LIMIT=NONE] -->
     <string name="volume_ringer_hint_mute">mute</string>
     <!-- Hint for accessibility. For example: double tap to unmute [CHAR_LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 14b376a..2d202fb 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -663,16 +663,16 @@
     </style>
 
     <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
-      <item name="android:windowAnimationStyle">@style/Animation.Fade</item>
+      <item name="android:windowAnimationStyle">@style/Animation.ControlDialog</item>
       <item name="android:windowFullscreen">true</item>
       <item name="android:windowIsFloating">false</item>
-      <item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
+      <item name="android:windowBackground">@null</item>
       <item name="android:backgroundDimEnabled">true</item>
     </style>
 
-    <style name="Animation.Fade">
-      <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
-      <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+    <style name="Animation.ControlDialog">
+      <item name="android:windowEnterAnimation">@*android:anim/dialog_enter</item>
+      <item name="android:windowExitAnimation">@*android:anim/dialog_exit</item>
     </style>
 
     <style name="Control" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index d9a1eb6..a4054be 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -305,6 +305,7 @@
     private boolean mLogoutEnabled;
     // cached value to avoid IPCs
     private boolean mIsUdfpsEnrolled;
+    private boolean mKeyguardQsUserSwitchEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1916,7 +1917,7 @@
             return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
                     && !isUdfpsEnrolled();
         }
-        return true;
+        return !isKeyguardQsUserSwitchEnabled();
     }
 
     /**
@@ -1926,6 +1927,17 @@
         return mIsUdfpsEnrolled;
     }
 
+    /**
+     * @return true if the keyguard qs user switcher shortcut is enabled
+     */
+    public boolean isKeyguardQsUserSwitchEnabled() {
+        return mKeyguardQsUserSwitchEnabled;
+    }
+
+    public void setKeyguardQsUserSwitchEnabled(boolean enabled) {
+        mKeyguardQsUserSwitchEnabled = enabled;
+    }
+
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
         @Override
         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 79f0688..2040347 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -40,6 +40,7 @@
 
 import androidx.annotation.WorkerThread;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dumpable;
@@ -78,6 +79,7 @@
     private static final boolean DEBUG = false;
 
     private final BroadcastDispatcher mDispatcher;
+    private final Context mContext;
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
     private final LocationManager mLocationManager;
@@ -141,6 +143,7 @@
         mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
         mLocationManager = context.getSystemService(LocationManager.class);
         mPackageManager = context.getPackageManager();
+        mContext = context;
         dumpManager.registerDumpable(TAG, this);
     }
 
@@ -357,6 +360,15 @@
         return mLocationProviderPackages.contains(packageName);
     }
 
+    private boolean isSpeechRecognizerUsage(int opCode, String packageName) {
+        if (AppOpsManager.OP_RECORD_AUDIO != opCode) {
+            return false;
+        }
+
+        return packageName.equals(
+                mContext.getString(R.string.config_systemSpeechRecognizer));
+    }
+
     // TODO ntmyren: remove after teamfood is finished
     private boolean shouldShowAppPredictor(String pkgName) {
         if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
@@ -388,7 +400,8 @@
         }
         // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
         if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
-                || shouldShowAppPredictor(packageName)) {
+                || shouldShowAppPredictor(packageName)
+                || isSpeechRecognizerUsage(appOpCode, packageName)) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 6451ad9..71fba33 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -68,6 +68,7 @@
 
     private final Context mContext;
     private final FingerprintManager mFingerprintManager;
+    @NonNull private final LayoutInflater mInflater;
     private final WindowManager mWindowManager;
     private final DelayableExecutor mFgExecutor;
     private final StatusBarStateController mStatusBarStateController;
@@ -75,10 +76,8 @@
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
     private final WindowManager.LayoutParams mCoreLayoutParams;
-    private final UdfpsView mView;
-    // Indicates whether the overlay is currently showing. Even if it has been requested, it might
-    // not be showing.
-    private boolean mIsOverlayShowing;
+
+    @Nullable private UdfpsView mView;
     // Indicates whether the overlay has been requested.
     private boolean mIsOverlayRequested;
     // Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
@@ -111,20 +110,48 @@
 
         @Override
         public void onEnrollmentProgress(int sensorId, int remaining) {
+            if (mView == null) {
+                return;
+            }
             mView.onEnrollmentProgress(remaining);
         }
 
         @Override
         public void onEnrollmentHelp(int sensorId) {
+            if (mView == null) {
+                return;
+            }
             mView.onEnrollmentHelp();
         }
 
         @Override
         public void setDebugMessage(int sensorId, String message) {
+            if (mView == null) {
+                return;
+            }
             mView.setDebugMessage(message);
         }
     }
 
+    @VisibleForTesting
+    final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
+            (expansion, expanded) -> {
+                if (mView != null) {
+                    mView.onExpansionChanged(expansion, expanded);
+                }
+    };
+
+    @VisibleForTesting
+    final StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    if (mView != null) {
+                        mView.onStateChanged(newState);
+                    }
+                }
+    };
+
     @SuppressLint("ClickableViewAccessibility")
     private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
         UdfpsView view = (UdfpsView) v;
@@ -157,13 +184,14 @@
     @Inject
     public UdfpsController(@NonNull Context context,
             @Main Resources resources,
-            LayoutInflater inflater,
+            @NonNull LayoutInflater inflater,
             @Nullable FingerprintManager fingerprintManager,
             WindowManager windowManager,
             @NonNull StatusBarStateController statusBarStateController,
             @Main DelayableExecutor fgExecutor,
             @Nullable StatusBar statusBar) {
         mContext = context;
+        mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
         // fingerprint manager should never be null.
         mFingerprintManager = checkNotNull(fingerprintManager);
@@ -189,15 +217,10 @@
                 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
         mCoreLayoutParams.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
-        mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
-        mView.setSensorProperties(mSensorProps);
-        mView.setHbmCallback(this);
-
-        statusBar.addExpansionChangedListener(mView);
-        statusBarStateController.addCallback(mView);
+        statusBar.addExpansionChangedListener(mStatusBarExpansionListener);
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
 
         mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
-        mIsOverlayShowing = false;
     }
 
     @Nullable
@@ -220,7 +243,13 @@
      * @return where the UDFPS exists on the screen in pixels.
      */
     public RectF getSensorLocation() {
-        return mView.getSensorRect();
+        // This is currently used to calculate the amount of space available for notifications
+        // on lockscreen. Keyguard is only shown in portrait mode for now, so this will need to
+        // be updated if that ever changes.
+        return new RectF(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
     }
 
     private void showOverlay(int reason) {
@@ -298,14 +327,18 @@
 
     private void showUdfpsOverlay(int reason) {
         mFgExecutor.execute(() -> {
-            if (!mIsOverlayShowing) {
+            if (mView == null) {
                 try {
                     Log.v(TAG, "showUdfpsOverlay | adding window");
+
+                    mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
+                    mView.setSensorProperties(mSensorProps);
+                    mView.setHbmCallback(this);
+
                     final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
                     mView.setExtras(animation, mEnrollHelper);
                     mWindowManager.addView(mView, computeLayoutParams(animation));
                     mView.setOnTouchListener(mOnTouchListener);
-                    mIsOverlayShowing = true;
                 } catch (RuntimeException e) {
                     Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
                 }
@@ -334,14 +367,12 @@
 
     private void hideUdfpsOverlay() {
         mFgExecutor.execute(() -> {
-            if (mIsOverlayShowing) {
+            if (mView != null) {
                 Log.v(TAG, "hideUdfpsOverlay | removing window");
-                mView.setExtras(null, null);
-                mView.setOnTouchListener(null);
                 // Reset the controller back to its starting state.
                 onFingerUp();
                 mWindowManager.removeView(mView);
-                mIsOverlayShowing = false;
+                mView = null;
             } else {
                 Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
             }
@@ -388,12 +419,20 @@
 
     // This method can be called from the UI thread.
     private void onFingerDown(int x, int y, float minor, float major) {
+        if (mView == null) {
+            Log.w(TAG, "Null view in onFingerDown");
+            return;
+        }
         mView.startIllumination(() ->
                 mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
     }
 
     // This method can be called from the UI thread.
     private void onFingerUp() {
+        if (mView == null) {
+            Log.w(TAG, "Null view in onFingerUp");
+            return;
+        }
         mFingerprintManager.onPointerUp(mSensorProps.sensorId);
         mView.stopIllumination();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 6ffecdb..3997943 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -136,17 +136,13 @@
     }
 
     @Override
-    public void onExpandedChanged(boolean isExpanded) {
-        mNotificationShadeExpanded = isExpanded;
-    }
-
-    @Override
     public void onStateChanged(int newState) {
         mStatusBarState = newState;
     }
 
     @Override
     public void onExpansionChanged(float expansion, boolean expanded) {
+        mNotificationShadeExpanded = expanded;
         mAnimationView.onExpansionChanged(expansion, expanded);
     }
 
@@ -192,10 +188,6 @@
         }
     }
 
-    RectF getSensorRect() {
-        return new RectF(mSensorRect);
-    }
-
     void setDebugMessage(String message) {
         mDebugMessage = message;
         postInvalidate();
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
index cad166d..1ea1d97 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
@@ -41,7 +41,7 @@
 
 object ControlsAnimations {
 
-    private const val ALPHA_EXIT_DURATION = 167L
+    private const val ALPHA_EXIT_DURATION = 183L
     private const val ALPHA_ENTER_DELAY = ALPHA_EXIT_DURATION
     private const val ALPHA_ENTER_DURATION = 350L - ALPHA_ENTER_DELAY
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 1c2f17c..2d647a9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -313,6 +313,10 @@
             setOnClickListener {
                 val i = Intent().apply {
                     component = ComponentName(context, ControlsProviderSelectorActivity::class.java)
+                    putExtra(
+                        ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+                        backToGlobalActions
+                    )
                 }
                 if (doneButton.isEnabled) {
                     // The user has made changes
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 0814774..d5e41d0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.ui.ControlsDialog
+import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.globalactions.GlobalActionsComponent
@@ -49,13 +51,15 @@
     private val listingController: ControlsListingController,
     private val controlsController: ControlsController,
     private val globalActionsComponent: GlobalActionsComponent,
-    broadcastDispatcher: BroadcastDispatcher
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val uiController: ControlsUiController
 ) : LifecycleActivity() {
 
     companion object {
         private const val TAG = "ControlsProviderSelectorActivity"
     }
 
+    private var backToGlobalActions = true
     private lateinit var recyclerView: RecyclerView
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = listingController.currentUserId
@@ -101,10 +105,19 @@
             }
         }
         requireViewById<View>(R.id.done).visibility = View.GONE
+
+        backToGlobalActions = intent.getBooleanExtra(
+            ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+            true
+        )
     }
 
     override fun onBackPressed() {
-        globalActionsComponent.handleShowGlobalActionsMenu()
+        if (backToGlobalActions) {
+            globalActionsComponent.handleShowGlobalActionsMenu()
+        } else {
+            ControlsDialog(applicationContext, broadcastDispatcher).show(uiController)
+        }
         animateExitAndFinish()
     }
 
@@ -152,8 +165,13 @@
                             listingController.getAppLabel(it))
                     putExtra(Intent.EXTRA_COMPONENT_NAME, it)
                     putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+                    putExtra(
+                        ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
+                        backToGlobalActions
+                    )
                 }
                 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
+                animateExitAndFinish()
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
index b1c85b5..8ff7b7a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/NearestTouchFrame.java
@@ -84,19 +84,14 @@
     }
 
     @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
         mClickableChildren.clear();
         mAttachedChildren.clear();
         mTouchableRegions.clear();
         addClickableChildren(this);
-        cacheClosestChildLocations();
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
         getLocationInWindow(mOffset);
+        cacheClosestChildLocations();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index 2f9b17a..2ea8657 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -19,8 +19,6 @@
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
 import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
 
-import static com.android.systemui.people.PeopleSpaceUtils.getUserHandle;
-
 import android.app.Activity;
 import android.app.INotificationManager;
 import android.app.people.IPeopleManager;
@@ -155,7 +153,7 @@
                 if (DEBUG) Log.d(TAG, "Caching shortcut for PeopleTile: " + tile.getId());
                 mLauncherApps.cacheShortcuts(tile.getPackageName(),
                         Collections.singletonList(tile.getId()),
-                        getUserHandle(tile), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
+                        tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS);
             } catch (Exception e) {
                 Log.w(TAG, "Exception caching shortcut:" + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
index 9ae7847..6f89332 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
@@ -21,7 +21,6 @@
 import android.content.pm.LauncherApps;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
-import android.os.UserHandle;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -82,7 +81,7 @@
     public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) {
         mTileView.setOnClickListener(v ->
                 launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null,
-                        UserHandle.getUserHandleForUid(tile.getUid())));
+                        tile.getUserHandle()));
     }
 
     /** Sets the click listener of the tile directly. */
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5dda23e..41080af 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -55,7 +55,6 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.service.notification.ConversationChannelWrapper;
@@ -160,7 +159,6 @@
         List<ConversationChannelWrapper> conversations =
                 notificationManager.getConversations(
                         false).getList();
-
         // Add priority conversations to tiles list.
         Stream<ShortcutInfo> priorityConversations = conversations.stream()
                 .filter(c -> c.getNotificationChannel() != null
@@ -252,7 +250,11 @@
             }
 
             // If tile is null, we need to retrieve from persisted storage.
-            if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Retrieving from storage after reboots: " + shortcutId + " user: " + userId
+                                + " pkg: " + pkg);
+            }
             LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
             ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
             if (channel == null) {
@@ -384,7 +386,7 @@
             PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
         String shortcutId = tile.getId();
         String packageName = tile.getPackageName();
-        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        int userId = getUserId(tile);
         String key = getKey(shortcutId, packageName, userId);
         if (!visibleNotifications.containsKey(key)) {
             if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
@@ -641,7 +643,8 @@
             activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
             activityIntent.putExtra(
                     PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
-            activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
+            activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
+                    tile.getUserHandle());
             views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
                     context,
                     appWidgetId,
@@ -788,7 +791,6 @@
             Log.i(TAG, "ConversationChannel is null");
             return null;
         }
-
         PeopleSpaceTile tile = new PeopleSpaceTile.Builder(channel, launcherApps).build();
         if (!PeopleSpaceUtils.shouldKeepConversation(tile)) {
             Log.i(TAG, "PeopleSpaceTile is not valid");
@@ -1069,11 +1071,6 @@
 
     /** Returns the userId associated with a {@link PeopleSpaceTile} */
     public static int getUserId(PeopleSpaceTile tile) {
-        return getUserHandle(tile).getIdentifier();
-    }
-
-    /** Returns the {@link UserHandle} associated with a {@link PeopleSpaceTile} */
-    public static UserHandle getUserHandle(PeopleSpaceTile tile) {
-        return UserHandle.getUserHandleForUid(tile.getUid());
+        return tile.getUserHandle().getIdentifier();
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
index 358f311..13e30f9 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java
@@ -41,7 +41,8 @@
         Intent intent = getIntent();
         String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID);
         String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME);
-        int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0);
+        UserHandle userHandle = intent.getParcelableExtra(
+                PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE);
 
         if (tileId != null && !tileId.isEmpty()) {
             if (DEBUG) {
@@ -52,7 +53,7 @@
                 LauncherApps launcherApps =
                         getApplicationContext().getSystemService(LauncherApps.class);
                 launcherApps.startShortcut(
-                        packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid));
+                        packageName, tileId, null, null, userHandle);
             } catch (Exception e) {
                 Log.e(TAG, "Exception starting shortcut:" + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 3d1055f..90baf56 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -48,7 +48,7 @@
 
     public static final String EXTRA_TILE_ID = "extra_tile_id";
     public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
-    public static final String EXTRA_UID = "extra_uid";
+    public static final String EXTRA_USER_HANDLE = "extra_user_handle";
 
     public UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
index 80794cb..0503522 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java
@@ -123,7 +123,8 @@
             fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
             fillInIntent.putExtra(
                     PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
-            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
+            fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
+                    tile.getUserHandle());
             personView.setOnClickFillInIntent(R.id.item, fillInIntent);
         } catch (Exception e) {
             Log.e(TAG, "Couldn't retrieve shortcut information", e);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index eaf2123..fcb56a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -71,7 +71,6 @@
     private int mMinRows = 1;
     private int mMaxColumns = TileLayout.NO_MAX_COLUMNS;
 
-    private boolean mShowLabels = true;
     private final boolean mSideLabels;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
@@ -91,16 +90,6 @@
     }
     private int mLastMaxHeight = -1;
 
-    @Override
-    public void setShowLabels(boolean show) {
-        mShowLabels = show;
-        for (TileLayout p : mPages) {
-            p.setShowLabels(show);
-        }
-        mDistributeTiles = true;
-        requestLayout();
-    }
-
     public void saveInstanceState(Bundle outState) {
         outState.putInt(CURRENT_PAGE, getCurrentItem());
     }
@@ -239,7 +228,6 @@
                         : R.layout.qs_paged_page, this, false);
         page.setMinRows(mMinRows);
         page.setMaxColumns(mMaxColumns);
-        page.setShowLabels(mShowLabels);
         return page;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 16e5196..2bea72c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED;
+
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.Intent;
@@ -41,6 +43,7 @@
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * Controller for {@link QSFooterView}.
@@ -63,6 +66,8 @@
     private final View mEdit;
     private final MultiUserSwitch mMultiUserSwitch;
     private final PageIndicator mPageIndicator;
+    private final View mPowerMenuLite;
+    private final boolean mShowPMLiteButton;
 
     private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
             new UserInfoController.OnUserInfoChangedListener() {
@@ -123,7 +128,8 @@
             DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
             QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer,
             QuickQSPanelController quickQSPanelController,
-            TunerService tunerService, MetricsLogger metricsLogger) {
+            TunerService tunerService, MetricsLogger metricsLogger,
+            @Named(PM_LITE_ENABLED) boolean showPMLiteButton) {
         super(view);
         mUserManager = userManager;
         mUserInfoController = userInfoController;
@@ -141,10 +147,15 @@
         mEdit = mView.findViewById(android.R.id.edit);
         mMultiUserSwitch = mView.findViewById(R.id.multi_user_switch);
         mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
+        mPowerMenuLite = mView.findViewById(R.id.pm_lite);
+        mShowPMLiteButton = showPMLiteButton;
     }
 
     @Override
     protected void onViewAttached() {
+        if (mShowPMLiteButton) {
+            mPowerMenuLite.setVisibility(View.VISIBLE);
+        }
         mView.addOnLayoutChangeListener(
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
                         mView.updateAnimator(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0563307..7657dce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -847,8 +847,6 @@
         default void setExpansion(float expansion) {}
 
         int getNumVisibleTiles();
-
-        default void setShowLabels(boolean show) {}
     }
 
     interface OnConfigurationChangedListener {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index d60801e..eda1abb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -124,7 +124,6 @@
         updateMediaDisappearParameters();
 
         mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
-        mTunerService.addTunable(mTunable, QS_REMOVE_LABELS);
         mView.updateResources();
         if (mView.isListening()) {
             refreshAllTiles();
@@ -138,13 +137,6 @@
     }
 
     @Override
-    boolean switchTileLayout(boolean force) {
-        boolean result = super.switchTileLayout(force);
-        getTileLayout().setShowLabels(mShowLabels);
-        return result;
-    }
-
-    @Override
     protected QSTileRevealController createTileRevealController() {
         return mQsTileRevealControllerFactory.create(
                 this, (PagedTileLayout) mView.createRegularTileLayout());
@@ -152,7 +144,6 @@
 
     @Override
     protected void onViewDetached() {
-        mTunerService.removeTunable(mTunable);
         mTunerService.removeTunable(mView);
         mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
         if (mBrightnessMirrorController != null) {
@@ -318,22 +309,5 @@
     public boolean isExpanded() {
         return mView.isExpanded();
     }
-
-    private TunerService.Tunable mTunable = new TunerService.Tunable() {
-        @Override
-        public void onTuningChanged(String key, String newValue) {
-            if (QS_REMOVE_LABELS.equals(key)) {
-                if (!mQSLabelFlag) return;
-                boolean newShowLabels = newValue == null || "0".equals(newValue);
-                if (mShowLabels == newShowLabels) return;
-                mShowLabels = newShowLabels;
-                for (TileRecord t : mRecords) {
-                    t.tileView.setShowLabels(mShowLabels);
-                }
-                getTileLayout().setShowLabels(mShowLabels);
-                mView.requestLayout();
-            }
-        }
-    };
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index f1174fb..8ab1743 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -198,7 +198,6 @@
         final TileRecord r = new TileRecord();
         r.tile = tile;
         r.tileView = mHost.createTileView(tile, collapsedView);
-        r.tileView.setShowLabels(mShowLabels);
         mView.addTile(r);
         mRecords.add(r);
         mCachedSpecs = getTilesSpecs();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 4de4a78..c3cc3af 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -34,8 +34,6 @@
         }
     }
 
-    override fun setShowLabels(show: Boolean) { }
-
     override fun isFull(): Boolean {
         return mRecords.size >= maxTiles()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 14cbf98..c1ce4a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -36,7 +36,6 @@
     private int mCellMarginTop;
     protected boolean mListening;
     protected int mMaxAllowedRows = 3;
-    private boolean mShowLabels = true;
 
     // Prototyping with less rows
     private final boolean mLessRows;
@@ -57,12 +56,6 @@
     }
 
     @Override
-    public void setShowLabels(boolean show) {
-        mShowLabels = show;
-        updateResources();
-    }
-
-    @Override
     public int getOffsetTop(TileRecord tile) {
         return getTop();
     }
@@ -128,12 +121,9 @@
         mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
-        if (!mShowLabels && mCellMarginVertical == 0) {
-            mCellMarginVertical = mCellMarginHorizontal;
-        }
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
         mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
-        if (mLessRows && mShowLabels) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1);
+        if (mLessRows) mMaxAllowedRows = Math.max(mMinRows, mMaxAllowedRows - 1);
         if (updateColumns()) {
             requestLayout();
             return true;
@@ -194,9 +184,8 @@
                 + mCellMarginVertical;
         final int previousRows = mRows;
         mRows = availableHeight / (getCellHeight() + mCellMarginVertical);
-        final int minRows = mShowLabels ? mMinRows : mMinRows + 1;
-        if (mRows < minRows) {
-            mRows = minRows;
+        if (mRows < mMinRows) {
+            mRows = mMinRows;
         } else if (mRows >= mMaxAllowedRows) {
             mRows = mMaxAllowedRows;
         }
@@ -216,7 +205,7 @@
     }
 
     protected int getCellHeight() {
-        return mShowLabels ? mMaxCellHeight : mMaxCellHeight / 2;
+        return mMaxCellHeight;
     }
 
     protected void layoutTileRecords(int numRecords) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index a567f51..0dc0b30 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,6 +42,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 
 import java.util.function.Consumer;
 
@@ -74,30 +75,25 @@
     private final NetworkController.SignalCallback mSignalCallback =
             new NetworkController.SignalCallback() {
                 @Override
-                public void setMobileDataIndicators(NetworkController.IconState statusIcon,
-                        NetworkController.IconState qsIcon, int statusType, int qsType,
-                        boolean activityIn, boolean activityOut,
-                        CharSequence typeContentDescription,
-                        CharSequence typeContentDescriptionHtml, CharSequence description,
-                        boolean isWide, int subId, boolean roaming, boolean showTriangle) {
+                public void setMobileDataIndicators(MobileDataIndicators indicators) {
                     if (mProviderModel) {
                         return;
                     }
-                    int slotIndex = getSlotIndex(subId);
+                    int slotIndex = getSlotIndex(indicators.subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
                         return;
                     }
                     if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-                        Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
+                        Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId);
                         return;
                     }
                     mInfos[slotIndex] = new CellSignalState(
-                            statusIcon.visible,
-                            statusIcon.icon,
-                            statusIcon.contentDescription,
-                            typeContentDescription.toString(),
-                            roaming
+                            indicators.statusIcon.visible,
+                            indicators.statusIcon.icon,
+                            indicators.statusIcon.contentDescription,
+                            indicators.typeContentDescription.toString(),
+                            indicators.roaming
                     );
                     mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 9fe949b..dce081f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -25,7 +25,6 @@
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.Toolbar;
@@ -48,7 +47,6 @@
 public class QSCustomizer extends LinearLayout {
 
     static final int MENU_RESET = Menu.FIRST;
-    static final int MENU_REMOVE_LABELS = Menu.FIRST + 1;
     static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
 
     private final QSDetailClipper mClipper;
@@ -77,10 +75,6 @@
 
         toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
                 mContext.getString(com.android.internal.R.string.reset));
-        // Prototype menu item
-        toolbar.getMenu()
-                .add(Menu.NONE, MENU_REMOVE_LABELS, Menu.NONE, R.string.qs_remove_labels)
-                .setCheckable(true);
         toolbar.setTitle(R.string.qs_edit);
         mRecyclerView = findViewById(android.R.id.list);
         mTransparentView = findViewById(R.id.customizer_transparent_view);
@@ -89,11 +83,6 @@
         mRecyclerView.setItemAnimator(animator);
     }
 
-    MenuItem getRemoveItem() {
-        return ((Toolbar) findViewById(com.android.internal.R.id.action_bar))
-                .getMenu().findItem(MENU_REMOVE_LABELS);
-    }
-
     void updateResources() {
         LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams();
         lp.height = mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index d4bab21..f56a2bb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -17,9 +17,7 @@
 package com.android.systemui.qs.customize;
 
 import static com.android.systemui.qs.customize.QSCustomizer.EXTRA_QS_CUSTOMIZING;
-import static com.android.systemui.qs.customize.QSCustomizer.MENU_REMOVE_LABELS;
 import static com.android.systemui.qs.customize.QSCustomizer.MENU_RESET;
-import static com.android.systemui.qs.dagger.QSFlagsModule.QS_LABELS_FLAG;
 
 import android.content.res.Configuration;
 import android.os.Bundle;
@@ -38,7 +36,6 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSEditEvent;
 import com.android.systemui.qs.QSFragment;
-import com.android.systemui.qs.QSPanelController;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.statusbar.phone.LightBarController;
@@ -46,14 +43,12 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.ViewController;
 
 import java.util.ArrayList;
 import java.util.List;
 
 import javax.inject.Inject;
-import javax.inject.Named;
 
 /** {@link ViewController} for {@link QSCustomizer}. */
 @QSScope
@@ -67,8 +62,6 @@
     private final ConfigurationController mConfigurationController;
     private final UiEventLogger mUiEventLogger;
     private final Toolbar mToolbar;
-    private final TunerService mTunerService;
-    private final boolean mQsLabelsFlag;
 
     private final OnMenuItemClickListener mOnMenuItemClickListener = new OnMenuItemClickListener() {
         @Override
@@ -76,11 +69,6 @@
             if (item.getItemId() == MENU_RESET) {
                 mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET);
                 reset();
-            } else if (item.getItemId() == MENU_REMOVE_LABELS) {
-                item.setChecked(!item.isChecked());
-                mTunerService.setValue(
-                        QSPanelController.QS_REMOVE_LABELS, item.isChecked() ? "1" : "0");
-                return false;
             }
             return false;
         }
@@ -111,20 +99,11 @@
         }
     };
 
-    private final TunerService.Tunable mTunable = new TunerService.Tunable() {
-        @Override
-        public void onTuningChanged(String key, String newValue) {
-            mToolbar.getMenu().findItem(MENU_REMOVE_LABELS)
-                    .setChecked(newValue != null && !("0".equals(newValue)));
-        }
-    };
-
     @Inject
     protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
             QSTileHost qsTileHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
             KeyguardStateController keyguardStateController, LightBarController lightBarController,
-            ConfigurationController configurationController, UiEventLogger uiEventLogger,
-            TunerService tunerService, @Named(QS_LABELS_FLAG) boolean qsLabelsFlag) {
+            ConfigurationController configurationController, UiEventLogger uiEventLogger) {
         super(view);
         mTileQueryHelper = tileQueryHelper;
         mQsTileHost = qsTileHost;
@@ -136,21 +115,12 @@
         mUiEventLogger = uiEventLogger;
 
         mToolbar = mView.findViewById(com.android.internal.R.id.action_bar);
-        mQsLabelsFlag = qsLabelsFlag;
-
-        mTunerService = tunerService;
     }
 
-    @Override
-    protected void onInit() {
-        super.onInit();
-        mView.getRemoveItem().setVisible(mQsLabelsFlag);
-    }
 
     @Override
     protected void onViewAttached() {
         mView.updateNavBackDrop(getResources().getConfiguration(), mLightBarController);
-        mTunerService.addTunable(mTunable, QSPanelController.QS_REMOVE_LABELS);
 
         mConfigurationController.addCallback(mConfigurationListener);
 
@@ -181,7 +151,6 @@
 
     @Override
     protected void onViewDetached() {
-        mTunerService.removeTunable(mTunable);
         mTileQueryHelper.setListener(null);
         mToolbar.setOnMenuItemClickListener(null);
         mConfigurationController.removeCallback(mConfigurationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 35a8257..10192bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -21,6 +21,7 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.util.settings.GlobalSettings;
 
 import javax.inject.Named;
 
@@ -31,6 +32,8 @@
 public interface QSFlagsModule {
     String QS_LABELS_FLAG = "qs_labels_flag";
     String RBC_AVAILABLE = "rbc_available";
+    String PM_LITE_ENABLED = "pm_lite";
+    String PM_LITE_SETTING = "sysui_pm_lite";
 
     @Provides
     @SysUISingleton
@@ -46,4 +49,11 @@
     static boolean isReduceBrightColorsAvailable(Context context) {
         return ColorDisplayManager.isReduceBrightColorsAvailable(context);
     }
+
+    @Provides
+    @SysUISingleton
+    @Named(PM_LITE_ENABLED)
+    static boolean isPMLiteEnabled(FeatureFlags featureFlags, GlobalSettings globalSettings) {
+        return featureFlags.isPMLiteEnabled() && globalSettings.getInt(PM_LITE_SETTING, 0) != 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 424aafa..207b25d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -174,9 +174,4 @@
         mLabelContainer.setClickable(false);
         mLabelContainer.setLongClickable(false);
     }
-
-    @Override
-    public void setShowLabels(boolean show) {
-        mHandler.post(() -> mLabelContainer.setVisibility(show ? VISIBLE : GONE));
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index c98de8c..07d48f3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tileimpl
 
+import android.animation.ValueAnimator
 import android.content.Context
 import android.content.res.ColorStateList
 import android.graphics.Color
@@ -31,12 +32,17 @@
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
 
+// Placeholder
+private const val CORNER_RADIUS = 40f
+
 class QSTileViewHorizontal(
     context: Context,
     icon: QSIconView
 ) : QSTileView(context, icon, false) {
 
     private var paintDrawable: PaintDrawable? = null
+    private var paintColor = Color.TRANSPARENT
+    private var paintAnimator: ValueAnimator? = null
 
     init {
         orientation = HORIZONTAL
@@ -70,8 +76,8 @@
     override fun newTileBackground(): Drawable? {
         val d = super.newTileBackground()
         if (paintDrawable == null) {
-            paintDrawable = PaintDrawable(Color.WHITE).apply {
-                setCornerRadius(50f)
+            paintDrawable = PaintDrawable(paintColor).apply {
+                setCornerRadius(CORNER_RADIUS)
             }
         }
         if (d is RippleDrawable) {
@@ -94,9 +100,39 @@
 
     override fun handleStateChanged(state: QSTile.State) {
         super.handleStateChanged(state)
-        paintDrawable?.setTint(getCircleColor(state.state))
         mSecondLine.setTextColor(mLabel.textColors)
         mLabelContainer.background = null
+
+        val allowAnimations = animationsEnabled() && paintColor != Color.TRANSPARENT
+        val newColor = getCircleColor(state.state)
+        if (allowAnimations) {
+            animateToNewState(newColor)
+        } else {
+            if (newColor != paintColor) {
+                clearAnimator()
+                paintDrawable?.paint?.color = newColor
+                paintDrawable?.invalidateSelf()
+            }
+        }
+        paintColor = newColor
+    }
+
+    private fun animateToNewState(newColor: Int) {
+        if (newColor != paintColor) {
+            clearAnimator()
+            paintAnimator = ValueAnimator.ofArgb(paintColor, newColor)
+                .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+                    addUpdateListener { animation: ValueAnimator ->
+                        paintDrawable?.paint?.color = animation.animatedValue as Int
+                        paintDrawable?.invalidateSelf()
+                    }
+                    start()
+                }
+        }
+    }
+
+    private fun clearAnimator() {
+        paintAnimator?.cancel()?.also { paintAnimator = null }
     }
 
     override fun handleExpand(dualTarget: Boolean) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 1dddc45..f03ce2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
@@ -269,13 +270,10 @@
     private final NetworkController.SignalCallback mSignalCallback =
             new NetworkController.SignalCallback() {
                 @Override
-                public void setWifiIndicators(boolean enabled,
-                        NetworkController.IconState statusIcon,
-                        NetworkController.IconState qsIcon, boolean activityIn, boolean activityOut,
-                        String description, boolean isTransient, String statusLabel) {
+                public void setWifiIndicators(WifiIndicators indicators) {
                     // statusIcon.visible has the connected status information
-                    boolean enabledAndConnected =
-                            enabled && (qsIcon == null ? false : qsIcon.visible);
+                    boolean enabledAndConnected = indicators.enabled
+                            && (indicators.qsIcon == null ? false : indicators.qsIcon.visible);
                     if (enabledAndConnected != mWifiConnected) {
                         mWifiConnected = enabledAndConnected;
                         // Hotspot is not connected, so changes here should update
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 720c5dc..6a574d1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -56,6 +56,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import javax.inject.Inject;
@@ -264,21 +265,17 @@
         private final CallbackInfo mInfo = new CallbackInfo();
 
         @Override
-        public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-                int qsType, boolean activityIn, boolean activityOut,
-                CharSequence typeContentDescription,
-                CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
-            if (qsIcon == null) {
+        public void setMobileDataIndicators(MobileDataIndicators indicators) {
+            if (indicators.qsIcon == null) {
                 // Not data sim, don't display.
                 return;
             }
             mInfo.dataSubscriptionName = mController.getMobileDataNetworkName();
-            mInfo.dataContentDescription =
-                    (description != null) ? typeContentDescriptionHtml : null;
-            mInfo.activityIn = activityIn;
-            mInfo.activityOut = activityOut;
-            mInfo.roaming = roaming;
+            mInfo.dataContentDescription = indicators.description != null
+                    ? indicators.typeContentDescriptionHtml : null;
+            mInfo.activityIn = indicators.activityIn;
+            mInfo.activityOut = indicators.activityOut;
+            mInfo.roaming = indicators.roaming;
             mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
             refreshState(mInfo);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 946d041..e1a1fd2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -51,7 +51,9 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 import com.android.systemui.statusbar.policy.WifiIcons;
 
 import java.io.FileDescriptor;
@@ -234,70 +236,44 @@
 
 
         @Override
-        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-                boolean activityIn, boolean activityOut, String description, boolean isTransient,
-                String statusLabel) {
+        public void setWifiIndicators(WifiIndicators indicators) {
             if (DEBUG) {
-                Log.d(TAG, "setWifiIndicators: "
-                        + "enabled = " + enabled + ","
-                        + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
-                        + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
-                        + "activityIn = " + activityIn + ","
-                        + "activityOut = " + activityOut + ","
-                        + "description = " + description + ","
-                        + "isTransient = " + isTransient + ","
-                        + "statusLabel = " + statusLabel);
+                Log.d(TAG, "setWifiIndicators: " + indicators);
             }
-            mWifiInfo.mEnabled = enabled;
-            if (qsIcon == null) {
+            mWifiInfo.mEnabled = indicators.enabled;
+            if (indicators.qsIcon == null) {
                 return;
             }
-            mWifiInfo.mConnected = qsIcon.visible;
-            mWifiInfo.mWifiSignalIconId = qsIcon.icon;
-            mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
-            mWifiInfo.mSsid = description;
-            mWifiInfo.mActivityIn = activityIn;
-            mWifiInfo.mActivityOut = activityOut;
-            mWifiInfo.mIsTransient = isTransient;
-            mWifiInfo.mStatusLabel = statusLabel;
+            mWifiInfo.mConnected = indicators.qsIcon.visible;
+            mWifiInfo.mWifiSignalIconId = indicators.qsIcon.icon;
+            mWifiInfo.mWifiSignalContentDescription = indicators.qsIcon.contentDescription;
+            mWifiInfo.mEnabled = indicators.enabled;
+            mWifiInfo.mSsid = indicators.description;
+            mWifiInfo.mActivityIn = indicators.activityIn;
+            mWifiInfo.mActivityOut = indicators.activityOut;
+            mWifiInfo.mIsTransient = indicators.isTransient;
+            mWifiInfo.mStatusLabel = indicators.statusLabel;
             refreshState(mWifiInfo);
         }
 
         @Override
-        public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-                int qsType, boolean activityIn, boolean activityOut,
-                CharSequence typeContentDescription,
-                CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
+        public void setMobileDataIndicators(MobileDataIndicators indicators) {
             if (DEBUG) {
-                Log.d(TAG, "setMobileDataIndicators: "
-                        + "statusIcon = " + (statusIcon == null ? "" :  statusIcon.toString()) + ","
-                        + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
-                        + "statusType = " + statusType + ","
-                        + "qsType = " + qsType + ","
-                        + "activityIn = " + activityIn + ","
-                        + "activityOut = " + activityOut + ","
-                        + "typeContentDescription = " + typeContentDescription + ","
-                        + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + ","
-                        + "description = " + description + ","
-                        + "isWide = " + isWide + ","
-                        + "subId = " + subId + ","
-                        + "roaming = " + roaming + ","
-                        + "showTriangle = " + showTriangle);
+                Log.d(TAG, "setMobileDataIndicators: " + indicators);
             }
-            if (qsIcon == null) {
+            if (indicators.qsIcon == null) {
                 // Not data sim, don't display.
                 return;
             }
-            mCellularInfo.mDataSubscriptionName =
-                    description == null ? mController.getMobileDataNetworkName() : description;
-            mCellularInfo.mDataContentDescription =
-                    (description != null) ? typeContentDescriptionHtml : null;
-            mCellularInfo.mMobileSignalIconId = qsIcon.icon;
-            mCellularInfo.mQsTypeIcon = qsType;
-            mCellularInfo.mActivityIn = activityIn;
-            mCellularInfo.mActivityOut = activityOut;
-            mCellularInfo.mRoaming = roaming;
+            mCellularInfo.mDataSubscriptionName = indicators.description == null
+                    ? mController.getMobileDataNetworkName() : indicators.description;
+            mCellularInfo.mDataContentDescription = indicators.description != null
+                    ? indicators.typeContentDescriptionHtml : null;
+            mCellularInfo.mMobileSignalIconId = indicators.qsIcon.icon;
+            mCellularInfo.mQsTypeIcon = indicators.qsType;
+            mCellularInfo.mActivityIn = indicators.activityIn;
+            mCellularInfo.mActivityOut = indicators.activityOut;
+            mCellularInfo.mRoaming = indicators.roaming;
             mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1;
             refreshState(mCellularInfo);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index a6fd011..341e67c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -51,8 +51,8 @@
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 import com.android.systemui.statusbar.policy.WifiIcons;
 import com.android.wifitrackerlib.WifiEntry;
 
@@ -303,22 +303,20 @@
         final CallbackInfo mInfo = new CallbackInfo();
 
         @Override
-        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-                boolean activityIn, boolean activityOut, String description, boolean isTransient,
-                String statusLabel) {
-            if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled);
-            if (qsIcon == null) {
+        public void setWifiIndicators(WifiIndicators indicators) {
+            if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + indicators.enabled);
+            if (indicators.qsIcon == null) {
                 return;
             }
-            mInfo.enabled = enabled;
-            mInfo.connected = qsIcon.visible;
-            mInfo.wifiSignalIconId = qsIcon.icon;
-            mInfo.ssid = description;
-            mInfo.activityIn = activityIn;
-            mInfo.activityOut = activityOut;
-            mInfo.wifiSignalContentDescription = qsIcon.contentDescription;
-            mInfo.isTransient = isTransient;
-            mInfo.statusLabel = statusLabel;
+            mInfo.enabled = indicators.enabled;
+            mInfo.connected = indicators.qsIcon.visible;
+            mInfo.wifiSignalIconId = indicators.qsIcon.icon;
+            mInfo.ssid = indicators.description;
+            mInfo.activityIn = indicators.activityIn;
+            mInfo.activityOut = indicators.activityOut;
+            mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription;
+            mInfo.isTransient = indicators.isTransient;
+            mInfo.statusLabel = indicators.statusLabel;
             if (isShowingDetail()) {
                 mDetailAdapter.updateItems();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 131fde6..805ac7c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -325,6 +325,7 @@
 
         attachWindow();
         mWindow.setContentView(mScreenshotView);
+        mScreenshotView.requestApplyInsets();
 
         mScreenshotView.takePartialScreenshot(
                 rect -> takeScreenshotInternal(finisher, rect));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index dc639dc..54b99bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -21,24 +21,25 @@
 import static java.lang.Math.min;
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.BinderThread;
 import android.annotation.UiContext;
 import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.graphics.PixelFormat;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IScrollCaptureConnection;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.view.ScrollCaptureViewSupport;
 
 import java.util.function.Consumer;
 
@@ -62,16 +63,19 @@
      */
     public interface Connection {
         /**
-         * Session start should be deferred until UI is active because of resource allocation and
-         * potential visible side effects in the target window.
-         *
+         * Start a session.
+
          * @param sessionConsumer listener to receive the session once active
          * @param maxPages the capture buffer size expressed as a multiple of the content height
          */
+        // TODO ListenableFuture
         void start(Consumer<Session> sessionConsumer, float maxPages);
 
         /**
-         * Close the connection.
+         * Close the connection. Must end capture if started to avoid potential unwanted visual
+         * artifacts.
+         *
+         * @see Session#end(Runnable)
          */
         void close();
     }
@@ -119,6 +123,7 @@
          * @param top the top (y) position of the tile to capture, in content rect space
          * @param consumer listener to be informed of the result
          */
+        // TODO ListenableFuture
         void requestTile(int top, Consumer<CaptureResult> consumer);
 
         /**
@@ -129,16 +134,31 @@
          */
         int getMaxTiles();
 
+        /**
+         * @return the height of each image tile
+         */
         int getTileHeight();
 
+        /**
+         * @return the height of scrollable content being captured
+         */
         int getPageHeight();
 
+        /**
+         * @return the width of the scrollable page
+         */
         int getPageWidth();
 
         /**
+         * @return the bounds on screen of the window being captured.
+         */
+        Rect getWindowBounds();
+
+        /**
          * End the capture session, return the target app to original state. The listener
          * will be called when the target app is ready to before visible and interactive.
          */
+        // TODO ListenableFuture
         void end(Runnable listener);
     }
 
@@ -185,13 +205,13 @@
                         + ", taskId=" + taskId + ", consumer=" + consumer + ")");
             }
             mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
-                    new ControllerCallbacks(consumer));
+                    new ClientCallbacks(consumer));
         } catch (RemoteException e) {
             Log.e(TAG, "Ignored remote exception", e);
         }
     }
 
-    private static class ControllerCallbacks extends IScrollCaptureCallbacks.Stub implements
+    private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements
             Connection, Session, IBinder.DeathRecipient {
 
         private IScrollCaptureConnection mConnection;
@@ -206,46 +226,63 @@
         private int mTileWidth;
         private Rect mRequestRect;
         private boolean mStarted;
+
+        private ICancellationSignal mCancellationSignal;
+        private Rect mWindowBounds;
+        private Rect mBoundsInWindow;
         private int mMaxTiles;
 
-        private ControllerCallbacks(Consumer<Connection> connectionConsumer) {
+        private ClientCallbacks(Consumer<Connection> connectionConsumer) {
             mConnectionConsumer = connectionConsumer;
         }
 
-        // IScrollCaptureCallbacks
-
+        @BinderThread
         @Override
-        public void onConnected(IScrollCaptureConnection connection, Rect scrollBounds,
-                Point positionInWindow) throws RemoteException {
+        public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onConnected(connection=" + connection + ", scrollBounds=" + scrollBounds
-                        + ", positionInWindow=" + positionInWindow + ")");
+                Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")");
             }
-            mConnection = connection;
-            mConnection.asBinder().linkToDeath(this, 0);
-            mScrollBounds = scrollBounds;
-            mConnectionConsumer.accept(this);
+            if (response.isConnected()) {
+                mConnection = response.getConnection();
+                mConnection.asBinder().linkToDeath(this, 0);
+                mWindowBounds = response.getWindowBounds();
+                mBoundsInWindow = response.getBoundsInWindow();
+
+                int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
+                int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
+                mTileWidth = mBoundsInWindow.width();
+                mTileHeight = pxPerTile  / mBoundsInWindow.width();
+                if (DEBUG_SCROLL) {
+                    Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
+                    Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
+                    Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px");
+                }
+                mConnectionConsumer.accept(this);
+            }
             mConnectionConsumer = null;
-
-            int pxPerPage = mScrollBounds.width() * mScrollBounds.height();
-            int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
-            mTileWidth = mScrollBounds.width();
-            mTileHeight = pxPerTile  / mScrollBounds.width();
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "scrollBounds: " + mScrollBounds);
-                Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight);
-            }
         }
 
         @Override
-        public void onUnavailable() throws RemoteException {
+        public void start(Consumer<Session> sessionConsumer, float maxPages) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onUnavailable");
+                Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
+                        + " maxPages=" + maxPages + ")");
             }
-            // The targeted app does not support scroll capture
-            // or the window could not be found... etc etc.
+            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
+                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+            mSessionConsumer = sessionConsumer;
+
+            try {
+                mCancellationSignal = mConnection.startCapture(mReader.getSurface());
+                mStarted = true;
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to start", e);
+                mReader.close();
+            }
         }
 
+        @BinderThread
         @Override
         public void onCaptureStarted() {
             if (DEBUG_SCROLL) {
@@ -256,13 +293,25 @@
         }
 
         @Override
-        public void onCaptureBufferSent(long frameNumber, Rect contentArea) {
-            Image image = null;
-            if (frameNumber != ScrollCaptureViewSupport.NO_FRAME_PRODUCED) {
-                image = mReader.acquireNextImage();
-            }
+        public void requestTile(int top, Consumer<CaptureResult> consumer) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "onCaptureBufferSent(frameNumber=" + frameNumber
+                Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
+            }
+            cancelPendingRequest();
+            mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
+            mResultConsumer = consumer;
+            try {
+                mCancellationSignal = mConnection.requestImage(mRequestRect);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Caught remote exception from requestImage", e);
+            }
+        }
+
+        @Override
+        public void onImageRequestCompleted(int flags, Rect contentArea) {
+            Image image = mReader.acquireLatestImage();
+            if (DEBUG_SCROLL) {
+                Log.d(TAG, "onCaptureBufferSent(flags=" + flags
                         + ", contentArea=" + contentArea + ") image=" + image);
             }
             // Save and clear first, since the consumer will likely request the next
@@ -273,61 +322,13 @@
         }
 
         @Override
-        public void onConnectionClosed() {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "onConnectionClosed()");
-            }
-            disconnect();
-            if (mShutdownListener != null) {
-                mShutdownListener.run();
-                mShutdownListener = null;
-            }
-        }
-
-        // Misc
-
-        private void disconnect() {
-            if (mConnection != null) {
-                mConnection.asBinder().unlinkToDeath(this, 0);
-            }
-            mConnection = null;
-        }
-
-        // ScrollCaptureController.Connection
-
-        @Override
-        public void start(Consumer<Session> sessionConsumer, float maxPages) {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
-                        + " maxPages=" + maxPages + ")"
-                        + " [maxHeight: " + (mMaxTiles * mTileHeight) + "px]");
-            }
-            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
-            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
-                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
-            mSessionConsumer = sessionConsumer;
-            try {
-                mConnection.startCapture(mReader.getSurface());
-                mStarted = true;
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to start", e);
-            }
-        }
-
-        @Override
-        public void close() {
-            end(null);
-        }
-
-        // ScrollCaptureController.Session
-
-        @Override
         public void end(Runnable listener) {
             if (DEBUG_SCROLL) {
                 Log.d(TAG, "end(listener=" + listener + ")");
             }
             if (mStarted) {
                 mShutdownListener = listener;
+                mReader.close();
                 try {
                     // listener called from onConnectionClosed callback
                     mConnection.endCapture();
@@ -342,40 +343,37 @@
             }
         }
 
+        @BinderThread
         @Override
-        public int getPageHeight() {
-            return mScrollBounds.height();
-        }
-
-        @Override
-        public int getPageWidth() {
-            return mScrollBounds.width();
-        }
-
-        @Override
-        public int getTileHeight() {
-            return mTileHeight;
-        }
-
-        @Override
-        public int getMaxTiles() {
-            return mMaxTiles;
-        }
-
-        @Override
-        public void requestTile(int top, Consumer<CaptureResult> consumer) {
-            if (DEBUG_SCROLL) {
-                Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
+        public void onCaptureEnded() {
+            close();
+            if (mShutdownListener != null) {
+                mShutdownListener.run();
+                mShutdownListener = null;
             }
-            mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
-            mResultConsumer = consumer;
-            try {
-                mConnection.requestImage(mRequestRect);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Caught remote exception from requestImage", e);
+        }
+
+        @Override
+        public void close() {
+            if (mConnection != null) {
+                try {
+                    mConnection.close();
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                disconnect();
             }
         }
 
+        // Misc
+
+        private void disconnect() {
+            if (mConnection != null) {
+                mConnection.asBinder().unlinkToDeath(this, 0);
+            }
+            mConnection = null;
+        }
+
         /**
          * The process hosting the window went away abruptly!
          */
@@ -386,5 +384,40 @@
             }
             disconnect();
         }
+
+        @Override
+        public int getPageHeight() {
+            return mBoundsInWindow.height();
+        }
+
+        @Override
+        public int getPageWidth() {
+            return mBoundsInWindow.width();
+        }
+
+        @Override
+        public int getTileHeight() {
+            return mTileHeight;
+        }
+
+        public Rect getWindowBounds() {
+            return new Rect(mWindowBounds);
+        }
+
+        @Override
+        public int getMaxTiles() {
+            return mMaxTiles;
+        }
+
+        private void cancelPendingRequest() {
+            if (mCancellationSignal != null) {
+                try {
+                    mCancellationSignal.cancel();
+                } catch (RemoteException e) {
+                    /* ignore */
+                }
+                mCancellationSignal = null;
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index cf77e29..1d59257 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -86,4 +86,8 @@
     public boolean isNavigationBarOverlayEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_navigation_bar_overlay);
     }
+
+    public boolean isPMLiteEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_pm_lite);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7e2d27a..a4e97a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -41,6 +42,7 @@
 import android.content.IntentFilter;
 import android.content.pm.UserInfo;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.face.FaceManager;
@@ -277,10 +279,7 @@
         // avoid calling this method since it has an IPC
         if (whitelistIpcs(this::isOrganizationOwnedDevice)) {
             final CharSequence organizationName = getOrganizationOwnedDeviceOrganizationName();
-            final CharSequence disclosure =  organizationName != null
-                    ? mContext.getResources().getString(R.string.do_disclosure_with_name,
-                    organizationName)
-                    : mContext.getResources().getText(R.string.do_disclosure_generic);
+            final CharSequence disclosure = getDisclosureText(organizationName);
             mRotateTextViewController.updateIndication(
                     INDICATION_TYPE_DISCLOSURE,
                     new KeyguardIndication.Builder()
@@ -297,6 +296,22 @@
         }
     }
 
+    private CharSequence getDisclosureText(@Nullable CharSequence organizationName) {
+        final Resources packageResources = mContext.getResources();
+        if (organizationName == null) {
+            return packageResources.getText(R.string.do_disclosure_generic);
+        } else if (mDevicePolicyManager.isDeviceManaged()
+                && mDevicePolicyManager.getDeviceOwnerType(
+                mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+                == DEVICE_OWNER_TYPE_FINANCED) {
+            return packageResources.getString(R.string.do_financed_disclosure_with_name,
+                    organizationName);
+        } else {
+            return packageResources.getString(R.string.do_disclosure_with_name,
+                    organizationName);
+        }
+    }
+
     private void updateOwnerInfo() {
         if (!isKeyguardLayoutEnabled()) {
             mRotateTextViewController.hideIndication(INDICATION_TYPE_OWNER_INFO);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index f427ba9..7691761 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -17,15 +17,11 @@
 package com.android.systemui.statusbar.notification.row;
 
 
-import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
-import static android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -1317,7 +1313,7 @@
 
     private boolean isBubblesEnabled() {
         return Settings.Global.getInt(mContext.getContentResolver(),
-                NOTIFICATION_BUBBLES, 0) == 1;
+                Settings.Global.NOTIFICATION_BUBBLES, 0) == 1;
     }
 
     /**
@@ -1373,27 +1369,26 @@
         }
         ImageView snoozeButton = layout.findViewById(com.android.internal.R.id.snooze_button);
         View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
-        LinearLayout actionContainerLayout =
-                layout.findViewById(com.android.internal.R.id.actions_container_layout);
-        if (snoozeButton == null || actionContainer == null || actionContainerLayout == null) {
+        if (snoozeButton == null || actionContainer == null) {
             return;
         }
         final boolean showSnooze = Settings.Secure.getInt(mContext.getContentResolver(),
-                SHOW_NOTIFICATION_SNOOZE, 0) == 1;
-        if (!showSnooze) {
+                Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1;
+        // Notification.Builder can 'disable' the snooze button to prevent it from being shown here
+        boolean snoozeDisabled = !snoozeButton.isEnabled();
+        if (!showSnooze || snoozeDisabled) {
             snoozeButton.setVisibility(GONE);
             return;
         }
 
-        Resources res = mContext.getResources();
-        Drawable snoozeDrawable = res.getDrawable(R.drawable.ic_snooze);
+        Drawable snoozeDrawable = mContext.getDrawable(R.drawable.ic_snooze);
         mContainingNotification.updateNotificationColor();
         snoozeDrawable.setTint(mContainingNotification.getNotificationColor());
         snoozeButton.setImageDrawable(snoozeDrawable);
 
         final NotificationSnooze snoozeGuts = (NotificationSnooze) LayoutInflater.from(mContext)
                 .inflate(R.layout.notification_snooze, null, false);
-        final String snoozeDescription = res.getString(
+        final String snoozeDescription = mContext.getString(
                 R.string.notification_menu_snooze_description);
         final NotificationMenuRowPlugin.MenuItem snoozeMenuItem =
                 new NotificationMenuRow.NotificationMenuItem(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
index 414d620..222735a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapper.java
@@ -49,7 +49,7 @@
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
-        ensureThemeOnChildren();
+        ensureThemeOnChildren(mView);
 
         // Let's invert the notification colors when we're in night mode and
         // the notification background isn't colorized.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 301c372..d21ae13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -64,7 +64,7 @@
 
         // Custom views will most likely use just white or black as their text color.
         // We need to scan through and replace these colors by Material NEXT colors.
-        ensureThemeOnChildren();
+        ensureThemeOnChildren(mWrappedView);
 
         if (needsInversion(resolveBackgroundColor(), mWrappedView)) {
             invertViewLuminosity(mWrappedView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 0ef4c4d..89babf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -58,9 +58,10 @@
     private final Rect mTmpRect = new Rect();
 
     protected int mBackgroundColor = 0;
-    private int mLightTextColor;
-    private int mDarkTextColor;
-    private int mDefaultTextColor;
+    private int mMaterialTextColorPrimary;
+    private int mMaterialTextColorSecondary;
+    private int mThemedTextColorPrimary;
+    private int mThemedTextColorSecondary;
     private boolean mAdjustTheme;
 
     public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
@@ -124,15 +125,22 @@
             mBackgroundColor = backgroundColor;
             mView.setBackground(new ColorDrawable(Color.TRANSPARENT));
         }
-        mLightTextColor = mView.getContext().getColor(
-                com.android.internal.R.color.notification_primary_text_color_light);
-        mDarkTextColor = mView.getContext().getColor(
-                com.android.internal.R.color.notification_primary_text_color_dark);
+
+        Context materialTitleContext = new ContextThemeWrapper(mView.getContext(),
+                com.android.internal.R.style.TextAppearance_Material_Notification_Title);
+        mMaterialTextColorPrimary = Utils.getColorAttr(materialTitleContext,
+                com.android.internal.R.attr.textColor).getDefaultColor();
+        Context materialContext = new ContextThemeWrapper(mView.getContext(),
+                com.android.internal.R.style.TextAppearance_Material_Notification);
+        mMaterialTextColorSecondary = Utils.getColorAttr(materialContext,
+                com.android.internal.R.attr.textColor).getDefaultColor();
 
         Context themedContext = new ContextThemeWrapper(mView.getContext(),
                 com.android.internal.R.style.Theme_DeviceDefault_DayNight);
-        mDefaultTextColor = Utils.getColorAttr(themedContext,
+        mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
                 com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+        mThemedTextColorSecondary = Utils.getColorAttr(themedContext,
+                com.android.internal.R.attr.textColorSecondary).getDefaultColor();
     }
 
     protected boolean needsInversion(int defaultBackgroundColor, View view) {
@@ -210,27 +218,30 @@
         return false;
     }
 
-    protected void ensureThemeOnChildren() {
-        if (!mAdjustTheme || mView == null) {
+    protected void ensureThemeOnChildren(View rootView) {
+        if (!mAdjustTheme || mView == null || rootView == null) {
             return;
         }
 
         // Notifications with custom backgrounds should not be adjusted
         if (mBackgroundColor != Color.TRANSPARENT
-                || getBackgroundColor(mView) != Color.TRANSPARENT) {
+                || getBackgroundColor(mView) != Color.TRANSPARENT
+                || getBackgroundColor(rootView) != Color.TRANSPARENT) {
             return;
         }
 
         // Now let's check if there's unprotected text somewhere, and apply the theme if we find it.
-        processTextColorRecursive(mView);
+        processTextColorRecursive(rootView);
     }
 
     private void processTextColorRecursive(View view) {
         if (view instanceof TextView) {
             TextView textView = (TextView) view;
             int foreground = textView.getCurrentTextColor();
-            if (foreground == mLightTextColor || foreground == mDarkTextColor) {
-                textView.setTextColor(mDefaultTextColor);
+            if (foreground == mMaterialTextColorPrimary) {
+                textView.setTextColor(mThemedTextColorPrimary);
+            } else if (foreground == mMaterialTextColorSecondary) {
+                textView.setTextColor(mThemedTextColorSecondary);
             }
         } else if (view instanceof ViewGroup) {
             ViewGroup viewGroup = (ViewGroup) view;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 093f57a..83c347b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -605,6 +605,7 @@
         mKeyguardQsUserSwitchEnabled =
                 mKeyguardUserSwitcherEnabled && mResources.getBoolean(
                         R.bool.config_keyguard_user_switch_opens_qs_details);
+        keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled);
         mView.setWillNotDraw(!DEBUG);
         mLayoutInflater = layoutInflater;
         mFalsingManager = falsingManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index dacd941..9ee7b09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -27,6 +27,8 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.tuner.TunerService;
@@ -143,24 +145,14 @@
     }
 
     @Override
-    public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-            boolean activityIn, boolean activityOut, String description, boolean isTransient,
-            String statusLabel) {
+    public void setWifiIndicators(WifiIndicators indicators) {
         if (DEBUG) {
-            Log.d(TAG, "setWifiIndicators: "
-                    + "enabled = " + enabled + ","
-                    + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
-                    + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
-                    + "activityIn = " + activityIn + ","
-                    + "activityOut = " + activityOut + ","
-                    + "description = " + description + ","
-                    + "isTransient = " + isTransient + ","
-                    + "statusLabel = " + statusLabel);
+            Log.d(TAG, "setWifiIndicators: " + indicators);
         }
-        boolean visible = statusIcon.visible && !mHideWifi;
-        boolean in = activityIn && mActivityEnabled && visible;
-        boolean out = activityOut && mActivityEnabled && visible;
-        mIsWifiEnabled = enabled;
+        boolean visible = indicators.statusIcon.visible && !mHideWifi;
+        boolean in = indicators.activityIn && mActivityEnabled && visible;
+        boolean out = indicators.activityOut && mActivityEnabled && visible;
+        mIsWifiEnabled = indicators.enabled;
 
         WifiIconState newState = mWifiIconState.copy();
 
@@ -174,10 +166,10 @@
             newState.resId = R.drawable.ic_qs_no_internet_available;
         } else {
             newState.visible = visible;
-            newState.resId = statusIcon.icon;
+            newState.resId = indicators.statusIcon.icon;
             newState.activityIn = in;
             newState.activityOut = out;
-            newState.contentDescription = statusIcon.contentDescription;
+            newState.contentDescription = indicators.statusIcon.contentDescription;
             MobileIconState first = getFirstMobileState();
             newState.signalSpacerVisible = first != null && first.typeId != 0;
         }
@@ -225,44 +217,28 @@
     }
 
     @Override
-    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-            int qsType, boolean activityIn, boolean activityOut,
-            CharSequence typeContentDescription,
-            CharSequence typeContentDescriptionHtml, CharSequence description,
-            boolean isWide, int subId, boolean roaming, boolean showTriangle) {
+    public void setMobileDataIndicators(MobileDataIndicators indicators) {
         if (DEBUG) {
-            Log.d(TAG, "setMobileDataIndicators: "
-                    + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
-                    + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
-                    + "statusType = " + statusType + ","
-                    + "qsType = " + qsType + ","
-                    + "activityIn = " + activityIn + ","
-                    + "activityOut = " + activityOut + ","
-                    + "typeContentDescription = " + typeContentDescription + ","
-                    + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + ","
-                    + "description = " + description + ","
-                    + "isWide = " + isWide + ","
-                    + "subId = " + subId + ","
-                    + "roaming = " + roaming + ","
-                    + "showTriangle = " + showTriangle);
+            Log.d(TAG, "setMobileDataIndicators: " + indicators);
         }
-        MobileIconState state = getState(subId);
+        MobileIconState state = getState(indicators.subId);
         if (state == null) {
             return;
         }
 
         // Visibility of the data type indicator changed
-        boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
+        boolean typeChanged = indicators.statusType != state.typeId
+                && (indicators.statusType == 0 || state.typeId == 0);
 
-        state.visible = statusIcon.visible && !mHideMobile;
-        state.strengthId = statusIcon.icon;
-        state.typeId = statusType;
-        state.contentDescription = statusIcon.contentDescription;
-        state.typeContentDescription = typeContentDescription;
-        state.showTriangle = showTriangle;
-        state.roaming = roaming;
-        state.activityIn = activityIn && mActivityEnabled;
-        state.activityOut = activityOut && mActivityEnabled;
+        state.visible = indicators.statusIcon.visible && !mHideMobile;
+        state.strengthId = indicators.statusIcon.icon;
+        state.typeId = indicators.statusType;
+        state.contentDescription = indicators.statusIcon.contentDescription;
+        state.typeContentDescription = indicators.typeContentDescription;
+        state.showTriangle = indicators.showTriangle;
+        state.roaming = indicators.roaming;
+        state.activityIn = indicators.activityIn && mActivityEnabled;
+        state.activityOut = indicators.activityOut && mActivityEnabled;
 
         if (DEBUG) {
             Log.d(TAG, "MobileIconStates: "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 528c0cb..b96cb5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -23,7 +23,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
@@ -119,63 +121,29 @@
     }
 
     @Override
-    public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
-            final IconState qsIcon, final boolean activityIn, final boolean activityOut,
-            final String description, boolean isTransient, String secondaryLabel) {
+    public void setWifiIndicators(final WifiIndicators indicators) {
         String log = new StringBuilder()
                 .append(SSDF.format(System.currentTimeMillis())).append(",")
-                .append("setWifiIndicators: ")
-                .append("enabled=").append(enabled).append(",")
-                .append("statusIcon=").append(statusIcon).append(",")
-                .append("qsIcon=").append(qsIcon).append(",")
-                .append("activityIn=").append(activityIn).append(",")
-                .append("activityOut=").append(activityOut).append(",")
-                .append("description=").append(description).append(",")
-                .append("isTransient=").append(isTransient).append(",")
-                .append("secondaryLabel=").append(secondaryLabel)
+                .append(indicators)
                 .toString();
         recordLastCallback(log);
         post(() -> {
             for (SignalCallback callback : mSignalCallbacks) {
-                callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
-                        description, isTransient, secondaryLabel);
+                callback.setWifiIndicators(indicators);
             }
         });
-
-
     }
 
     @Override
-    public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
-            final int statusType, final int qsType, final boolean activityIn,
-            final boolean activityOut, final CharSequence typeContentDescription,
-            CharSequence typeContentDescriptionHtml, final CharSequence description,
-            final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
+    public void setMobileDataIndicators(final MobileDataIndicators indicators) {
         String log = new StringBuilder()
                 .append(SSDF.format(System.currentTimeMillis())).append(",")
-                .append("setMobileDataIndicators: ")
-                .append("statusIcon=").append(statusIcon).append(",")
-                .append("qsIcon=").append(qsIcon).append(",")
-                .append("statusType=").append(statusType).append(",")
-                .append("qsType=").append(qsType).append(",")
-                .append("activityIn=").append(activityIn).append(",")
-                .append("activityOut=").append(activityOut).append(",")
-                .append("typeContentDescription=").append(typeContentDescription).append(",")
-                .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
-                .append(",")
-                .append("description=").append(description).append(",")
-                .append("isWide=").append(isWide).append(",")
-                .append("subId=").append(subId).append(",")
-                .append("roaming=").append(roaming).append(",")
-                .append("showTriangle=").append(showTriangle)
+                .append(indicators)
                 .toString();
         recordLastCallback(log);
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
-                signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
-                        activityIn, activityOut, typeContentDescription,
-                        typeContentDescriptionHtml, description, isWide, subId, roaming,
-                        showTriangle);
+                signalCluster.setMobileDataIndicators(indicators);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 1ab7652..6c097bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -59,6 +59,7 @@
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import java.io.PrintWriter;
@@ -402,10 +403,12 @@
             showDataIcon |= mCurrentState.roaming;
             IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
                     getCurrentIconId(), contentDescription);
-            callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+            MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+                    statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
                     mCurrentState.roaming, showTriangle);
+            callback.setMobileDataIndicators(mobileDataIndicators);
         } else {
             boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
             IconState statusIcon = new IconState(
@@ -432,10 +435,12 @@
             showDataIcon &= mCurrentState.isDefault || dataDisabled;
             int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
             boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
-            callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+            MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+                    statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
                     mCurrentState.roaming, showTriangle);
+            callback.setMobileDataIndicators(mobileDataIndicators);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 0a9fead..ef2ca98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -46,34 +46,117 @@
 
     boolean isRadioOn();
 
+    /**
+     * Wrapper class for all the WiFi signals used for WiFi indicators.
+     */
+    final class WifiIndicators {
+        public boolean enabled;
+        public IconState statusIcon;
+        public IconState qsIcon;
+        public boolean activityIn;
+        public boolean activityOut;
+        public String description;
+        public boolean isTransient;
+        public String statusLabel;
+
+        public WifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
+                boolean activityIn, boolean activityOut, String description,
+                boolean isTransient, String statusLabel) {
+            this.enabled = enabled;
+            this.statusIcon = statusIcon;
+            this.qsIcon = qsIcon;
+            this.activityIn = activityIn;
+            this.activityOut = activityOut;
+            this.description = description;
+            this.isTransient = isTransient;
+            this.statusLabel = statusLabel;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("WifiIndicators[")
+                .append("enabled=").append(enabled)
+                .append(",statusIcon=").append(statusIcon == null ? "" : statusIcon.toString())
+                .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
+                .append(",activityIn=").append(activityIn)
+                .append(",activityOut=").append(activityOut)
+                .append(",description=").append(description)
+                .append(",isTransient=").append(isTransient)
+                .append(",statusLabel=").append(statusLabel)
+                .append(']').toString();
+        }
+    }
+
+    /**
+     * Wrapper class for all the mobile signals used for mobile data indicators.
+     */
+    final class MobileDataIndicators {
+        public IconState statusIcon;
+        public IconState qsIcon;
+        public int statusType;
+        public int qsType;
+        public boolean activityIn;
+        public boolean activityOut;
+        public CharSequence typeContentDescription;
+        public CharSequence typeContentDescriptionHtml;
+        public CharSequence description;
+        public boolean isWide;
+        public int subId;
+        public boolean roaming;
+        public boolean showTriangle;
+
+        public MobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+                int qsType, boolean activityIn, boolean activityOut,
+                CharSequence typeContentDescription, CharSequence typeContentDescriptionHtml,
+                CharSequence description, boolean isWide, int subId, boolean roaming,
+                boolean showTriangle) {
+            this.statusIcon = statusIcon;
+            this.qsIcon = qsIcon;
+            this.statusType = statusType;
+            this.qsType = qsType;
+            this.activityIn = activityIn;
+            this.activityOut = activityOut;
+            this.typeContentDescription = typeContentDescription;
+            this.typeContentDescriptionHtml = typeContentDescriptionHtml;
+            this.description = description;
+            this.isWide = isWide;
+            this.subId = subId;
+            this.roaming = roaming;
+            this.showTriangle = showTriangle;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("MobileDataIndicators[")
+                .append("statusIcon=").append(statusIcon == null ? "" :  statusIcon.toString())
+                .append(",qsIcon=").append(qsIcon == null ? "" : qsIcon.toString())
+                .append(",statusType=").append(statusType)
+                .append(",qsType=").append(qsType)
+                .append(",activityIn=").append(activityIn)
+                .append(",activityOut=").append(activityOut)
+                .append(",typeContentDescription=").append(typeContentDescription)
+                .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+                .append(",description=").append(description)
+                .append(",isWide=").append(isWide)
+                .append(",subId=").append(subId)
+                .append(",roaming=").append(roaming)
+                .append(",showTriangle=").append(showTriangle)
+                .append(']').toString();
+        }
+    }
+
     public interface SignalCallback {
-        default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
-                boolean activityIn, boolean activityOut, String description, boolean isTransient,
-                String statusLabel) {}
+        /**
+         * Callback for listeners to be able to update the state of any UI tracking connectivity of
+         * WiFi networks.
+         */
+        default void setWifiIndicators(WifiIndicators wifiIndicators) {}
 
         /**
          * Callback for listeners to be able to update the state of any UI tracking connectivity
-         * @param statusIcon the icon that should be shown in the status bar
-         * @param qsIcon the icon to show in Quick Settings
-         * @param statusType the resId of the data type icon (e.g. LTE) to show in the status bar
-         * @param qsType similar to above, the resId of the data type icon to show in Quick Settings
-         * @param activityIn indicates whether there is inbound activity
-         * @param activityOut indicates outbound activity
-         * @param typeContentDescription the contentDescription of the data type
-         * @param typeContentDescriptionHtml the (possibly HTML-styled) contentDescription of the
-         *                                   data type. Suitable for display
-         * @param description description of the network (usually just the network name)
-         * @param isWide //TODO: unused?
-         * @param subId subscription ID for which to update the UI
-         * @param roaming indicates roaming
-         * @param showTriangle whether to show the mobile triangle the in status bar
+         * of Mobile networks.
          */
-        default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
-                int qsType, boolean activityIn, boolean activityOut,
-                CharSequence typeContentDescription,
-                CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
-        }
+        default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {}
 
         default void setSubs(List<SubscriptionInfo> subs) {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 9f92142..8eb1e64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -165,7 +165,7 @@
     private int mCurrentUserId;
 
     private OnSubscriptionsChangedListener mSubscriptionListener;
-
+    private NetworkCapabilities mLastDefaultNetworkCapabilities;
     // Handler that all broadcasts are received on.
     private final Handler mReceiverHandler;
     // Handler that all callbacks are made on.
@@ -315,6 +315,7 @@
             public void onLost(Network network) {
                 mLastNetwork = null;
                 mLastNetworkCapabilities = null;
+                mLastDefaultNetworkCapabilities = null;
                 String callback = new StringBuilder()
                         .append(SSDF.format(System.currentTimeMillis())).append(",")
                         .append("onLost: ")
@@ -341,6 +342,7 @@
                 }
                 mLastNetwork = network;
                 mLastNetworkCapabilities = networkCapabilities;
+                mLastDefaultNetworkCapabilities = networkCapabilities;
                 String callback = new StringBuilder()
                         .append(SSDF.format(System.currentTimeMillis())).append(",")
                         .append("onCapabilitiesChanged: ")
@@ -959,18 +961,17 @@
     private void updateConnectivity() {
         mConnectedTransports.clear();
         mValidatedTransports.clear();
-        for (NetworkCapabilities nc :
-                mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
-            for (int transportType : nc.getTransportTypes()) {
+        if (mLastDefaultNetworkCapabilities != null) {
+            for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) {
                 if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR
-                        && Utils.tryGetWifiInfoForVcn(nc) != null) {
+                        && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) {
                     mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI);
-                    if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                    if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
                         mValidatedTransports.set(NetworkCapabilities.TRANSPORT_WIFI);
                     }
                 } else {
                     mConnectedTransports.set(transportType);
-                    if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
+                    if (mLastDefaultNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
                         mValidatedTransports.set(transportType);
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 16998d7..8d72c9c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -38,7 +38,9 @@
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 
 import java.io.PrintWriter;
 import java.util.Objects;
@@ -113,18 +115,24 @@
                         mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
                                 : getQsCurrentIconId(), contentDescription);
             }
-            callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
+            WifiIndicators wifiIndicators = new WifiIndicators(
+                    mCurrentState.enabled, statusIcon, qsIcon,
                     ssidPresent && mCurrentState.activityIn,
                     ssidPresent && mCurrentState.activityOut,
-                    wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
+                    wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
+            );
+            callback.setWifiIndicators(wifiIndicators);
         } else {
             IconState qsIcon = new IconState(mCurrentState.connected,
                     mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
                             : getQsCurrentIconId(), contentDescription);
-            callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
+            WifiIndicators wifiIndicators = new WifiIndicators(
+                    mCurrentState.enabled, statusIcon, qsIcon,
                     ssidPresent && mCurrentState.activityIn,
                     ssidPresent && mCurrentState.activityOut,
-                    wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
+                    wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel
+            );
+            callback.setWifiIndicators(wifiIndicators);
         }
     }
 
@@ -149,10 +157,13 @@
                 mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
         CharSequence description =
                 mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
-        callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
+        MobileDataIndicators mobileDataIndicators = new MobileDataIndicators(
+                statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
                 dataContentDescriptionHtml, description, icons.isWide,
-                mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true);
+                mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true
+        );
+        callback.setMobileDataIndicators(mobileDataIndicators);
     }
 
     private int getCurrentIconIdForCarrierWifi() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index df54eab..25345d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -33,7 +33,11 @@
 
 import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -51,6 +55,9 @@
 import android.graphics.PixelFormat;
 import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RotateDrawable;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Debug;
@@ -81,6 +88,8 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
@@ -88,6 +97,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -99,6 +109,8 @@
 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.AlphaTintDrawableWrapper;
+import com.android.systemui.util.RoundedCornerProgressDrawable;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -124,8 +136,14 @@
     static final int DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS = 5000;
     static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
 
+    private static final int DRAWER_ANIMATION_DURATION_SHORT = 175;
+    private static final int DRAWER_ANIMATION_DURATION = 250;
+
     private final int mDialogShowAnimationDurationMs;
     private final int mDialogHideAnimationDurationMs;
+    private final int mRingerDrawerItemSize;
+    private final boolean mShowVibrate;
+    private final int mRingerCount;
     private final boolean mShowLowMediaVolumeIcon;
     private final boolean mChangeVolumeRowTintWhenInactive;
 
@@ -140,6 +158,30 @@
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
     private ViewGroup mRinger;
+
+    private ViewGroup mSelectedRingerContainer;
+    private ImageView mSelectedRingerIcon;
+
+    private ViewGroup mRingerDrawerContainer;
+    private ViewGroup mRingerDrawerMute;
+    private ViewGroup mRingerDrawerVibrate;
+    private ViewGroup mRingerDrawerNormal;
+    private ImageView mRingerDrawerMuteIcon;
+    private ImageView mRingerDrawerVibrateIcon;
+    private ImageView mRingerDrawerNormalIcon;
+
+    /**
+     * View that draws the 'selected' background behind one of the three ringer choices in the
+     * drawer.
+     */
+    private ViewGroup mRingerDrawerNewSelectionBg;
+
+    private final ValueAnimator mRingerDrawerIconColorAnimator = ValueAnimator.ofFloat(0f, 1f);
+    private ImageView mRingerDrawerIconAnimatingSelected;
+    private ImageView mRingerDrawerIconAnimatingDeselected;
+
+    private boolean mIsRingerDrawerOpen = false;
+
     private ImageButton mRingerIcon;
     private ViewGroup mODICaptionsView;
     private CaptionsToggleImageButton mODICaptionsIcon;
@@ -191,6 +233,12 @@
             mContext.getResources().getInteger(R.integer.config_dialogShowAnimationDurationMs);
         mDialogHideAnimationDurationMs =
             mContext.getResources().getInteger(R.integer.config_dialogHideAnimationDurationMs);
+        mRingerDrawerItemSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.volume_ringer_drawer_item_size);
+        mShowVibrate = mController.hasVibrator();
+
+        // Normal, mute, and possibly vibrate.
+        mRingerCount = mShowVibrate ? 3 : 2;
     }
 
     @Override
@@ -314,6 +362,20 @@
             mZenIcon = mRinger.findViewById(R.id.dnd_icon);
         }
 
+        mSelectedRingerIcon = mDialog.findViewById(R.id.volume_new_ringer_active_icon);
+        mSelectedRingerContainer = mDialog.findViewById(
+                R.id.volume_new_ringer_active_icon_container);
+
+        mRingerDrawerMute = mDialog.findViewById(R.id.volume_drawer_mute);
+        mRingerDrawerNormal = mDialog.findViewById(R.id.volume_drawer_normal);
+        mRingerDrawerVibrate = mDialog.findViewById(R.id.volume_drawer_vibrate);
+        mRingerDrawerMuteIcon = mDialog.findViewById(R.id.volume_drawer_mute_icon);
+        mRingerDrawerVibrateIcon = mDialog.findViewById(R.id.volume_drawer_vibrate_icon);
+        mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
+        mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
+
+        setupRingerDrawer();
+
         mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
         if (mODICaptionsView != null) {
             mODICaptionsIcon = mODICaptionsView.findViewById(R.id.odi_captions_icon);
@@ -475,40 +537,275 @@
 
         row.anim = null;
 
+        final LayerDrawable seekbarDrawable =
+                (LayerDrawable) mContext.getDrawable(R.drawable.volume_row_seekbar);
+
+        final LayerDrawable seekbarBgDrawable =
+                (LayerDrawable) seekbarDrawable.findDrawableByLayerId(android.R.id.background);
+
+        row.sliderBgSolid = seekbarBgDrawable.findDrawableByLayerId(
+                R.id.volume_seekbar_background_solid);
+
+        row.sliderBgIcon = (AlphaTintDrawableWrapper)
+                ((RotateDrawable) seekbarBgDrawable.findDrawableByLayerId(
+                        R.id.volume_seekbar_background_icon)).getDrawable();
+
+        final LayerDrawable seekbarProgressDrawable = (LayerDrawable)
+                ((RoundedCornerProgressDrawable) seekbarDrawable.findDrawableByLayerId(
+                        android.R.id.progress)).getDrawable();
+
+        row.sliderProgressSolid = seekbarProgressDrawable.findDrawableByLayerId(
+                R.id.volume_seekbar_progress_solid);
+
+        row.sliderProgressIcon = (AlphaTintDrawableWrapper)
+                ((RotateDrawable) seekbarProgressDrawable.findDrawableByLayerId(
+                        R.id.volume_seekbar_progress_icon)).getDrawable();
+
+        row.slider.setProgressDrawable(seekbarDrawable);
+        row.slider.setThumb(null);
+
         row.icon = row.view.findViewById(R.id.volume_row_icon);
-        row.icon.setImageResource(iconRes);
-        if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
-            row.icon.setOnClickListener(v -> {
-                Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
-                mController.setActiveStream(row.stream);
-                if (row.stream == AudioManager.STREAM_RING) {
-                    final boolean hasVibrator = mController.hasVibrator();
-                    if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
-                        if (hasVibrator) {
-                            mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+
+        row.setIcon(iconRes, mContext.getTheme());
+
+        if (row.icon != null) {
+            if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
+                row.icon.setOnClickListener(v -> {
+                    Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+                    mController.setActiveStream(row.stream);
+                    if (row.stream == AudioManager.STREAM_RING) {
+                        final boolean hasVibrator = mController.hasVibrator();
+                        if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+                            if (hasVibrator) {
+                                mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+                            } else {
+                                final boolean wasZero = row.ss.level == 0;
+                                mController.setStreamVolume(stream,
+                                        wasZero ? row.lastAudibleLevel : 0);
+                            }
                         } else {
-                            final boolean wasZero = row.ss.level == 0;
-                            mController.setStreamVolume(stream,
-                                    wasZero ? row.lastAudibleLevel : 0);
+                            mController.setRingerMode(
+                                    AudioManager.RINGER_MODE_NORMAL, false);
+                            if (row.ss.level == 0) {
+                                mController.setStreamVolume(stream, 1);
+                            }
                         }
                     } else {
-                        mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
-                        if (row.ss.level == 0) {
-                            mController.setStreamVolume(stream, 1);
-                        }
+                        final boolean vmute = row.ss.level == row.ss.levelMin;
+                        mController.setStreamVolume(stream,
+                                vmute ? row.lastAudibleLevel : row.ss.levelMin);
                     }
-                } else {
-                    final boolean vmute = row.ss.level == row.ss.levelMin;
-                    mController.setStreamVolume(stream,
-                            vmute ? row.lastAudibleLevel : row.ss.levelMin);
-                }
-                row.userAttempt = 0;  // reset the grace period, slider updates immediately
-            });
-        } else {
-            row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+                    row.userAttempt = 0;  // reset the grace period, slider updates immediately
+                });
+            } else {
+                row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
         }
     }
 
+    private void setRingerMode(int newRingerMode) {
+        Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
+        incrementManualToggleCount();
+        updateRingerH();
+        provideTouchFeedbackH(newRingerMode);
+        mController.setRingerMode(newRingerMode, false);
+        maybeShowToastH(newRingerMode);
+    }
+
+    private void setupRingerDrawer() {
+        mRingerDrawerContainer = mDialog.findViewById(R.id.volume_drawer_container);
+
+        if (mRingerDrawerContainer == null) {
+            return;
+        }
+
+        if (!mShowVibrate) {
+            mRingerDrawerVibrate.setVisibility(GONE);
+        }
+
+        // In portrait, add padding to the bottom to account for the height of the open ringer
+        // drawer.
+        if (!isLandscape()) {
+            mDialogView.setPadding(
+                    mDialogView.getPaddingLeft(),
+                    mDialogView.getPaddingTop(),
+                    mDialogView.getPaddingRight(),
+                    mDialogView.getPaddingBottom() + (mRingerCount - 1) * mRingerDrawerItemSize);
+        } else {
+            mDialogView.setPadding(
+                    mDialogView.getPaddingLeft() + (mRingerCount - 1) * mRingerDrawerItemSize,
+                    mDialogView.getPaddingTop(),
+                    mDialogView.getPaddingRight(),
+                    mDialogView.getPaddingBottom());
+        }
+
+        ((LinearLayout) mRingerDrawerContainer.findViewById(R.id.volume_drawer_options))
+                .setOrientation(isLandscape() ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
+
+        mSelectedRingerContainer.setOnClickListener(view -> {
+            if (mIsRingerDrawerOpen) {
+                hideRingerDrawer();
+            } else {
+                showRingerDrawer();
+            }
+        });
+
+        mRingerDrawerVibrate.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
+        mRingerDrawerMute.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_SILENT));
+        mRingerDrawerNormal.setOnClickListener(
+                new RingerDrawerItemClickListener(RINGER_MODE_NORMAL));
+
+        final int unselectedColor = Utils.getColorAccentDefaultColor(mContext);
+        final int selectedColor = Utils.getColorAttrDefaultColor(
+                mContext, android.R.attr.colorBackgroundFloating);
+
+        // Add an update listener that animates the deselected icon to the unselected color, and the
+        // selected icon to the selected color.
+        mRingerDrawerIconColorAnimator.addUpdateListener(
+                anim -> {
+                    final float currentValue = (float) anim.getAnimatedValue();
+                    final int curUnselectedColor = (int) ArgbEvaluator.getInstance().evaluate(
+                            currentValue, selectedColor, unselectedColor);
+                    final int curSelectedColor = (int) ArgbEvaluator.getInstance().evaluate(
+                            currentValue, unselectedColor, selectedColor);
+
+                    mRingerDrawerIconAnimatingDeselected.setColorFilter(curUnselectedColor);
+                    mRingerDrawerIconAnimatingSelected.setColorFilter(curSelectedColor);
+                });
+        mRingerDrawerIconColorAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mRingerDrawerIconAnimatingDeselected.clearColorFilter();
+                mRingerDrawerIconAnimatingSelected.clearColorFilter();
+            }
+        });
+        mRingerDrawerIconColorAnimator.setDuration(DRAWER_ANIMATION_DURATION_SHORT);
+    }
+
+    private ImageView getDrawerIconViewForMode(int mode) {
+        if (mode == RINGER_MODE_VIBRATE) {
+            return mRingerDrawerVibrateIcon;
+        } else if (mode == RINGER_MODE_SILENT) {
+            return mRingerDrawerMuteIcon;
+        } else {
+            return mRingerDrawerNormalIcon;
+        }
+    }
+
+    /**
+     * Translation to apply form the origin (either top or left) to overlap the selection background
+     * with the given mode in the drawer.
+     */
+    private float getTranslationInDrawerForRingerMode(int mode) {
+        return mode == RINGER_MODE_VIBRATE
+                ? -mRingerDrawerItemSize * 2
+                : mode == RINGER_MODE_SILENT
+                        ? -mRingerDrawerItemSize
+                        : 0;
+    }
+
+    /** Animates in the ringer drawer. */
+    private void showRingerDrawer() {
+        // Show all ringer icons except the currently selected one, since we're going to animate the
+        // ringer button to that position.
+        mRingerDrawerVibrateIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_VIBRATE ? INVISIBLE : VISIBLE);
+        mRingerDrawerMuteIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_SILENT ? INVISIBLE : VISIBLE);
+        mRingerDrawerNormalIcon.setVisibility(
+                mState.ringerModeInternal == RINGER_MODE_NORMAL ? INVISIBLE : VISIBLE);
+
+        // Hide the selection background - we use this to show a selection when one is
+        // tapped, so it should be invisible until that happens. However, position it below
+        // the currently selected ringer so that it's ready to animate.
+        mRingerDrawerNewSelectionBg.setAlpha(0f);
+
+        if (!isLandscape()) {
+            mRingerDrawerNewSelectionBg.setTranslationY(
+                    getTranslationInDrawerForRingerMode(mState.ringerModeInternal));
+        } else {
+            mRingerDrawerNewSelectionBg.setTranslationX(
+                    getTranslationInDrawerForRingerMode(mState.ringerModeInternal));
+        }
+
+        // Move the drawer so that the top/rightmost ringer choice overlaps with the selected ringer
+        // icon.
+        if (!isLandscape()) {
+            mRingerDrawerContainer.setTranslationY(mRingerDrawerItemSize * (mRingerCount - 1));
+        } else {
+            mRingerDrawerContainer.setTranslationX(mRingerDrawerItemSize * (mRingerCount - 1));
+        }
+        mRingerDrawerContainer.setAlpha(0f);
+        mRingerDrawerContainer.setVisibility(VISIBLE);
+
+        // Animate the drawer up and visible.
+        mRingerDrawerContainer.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                // Vibrate is way farther up, so give the selected ringer icon a head start if
+                // vibrate is selected.
+                .setDuration(mState.ringerModeInternal == RINGER_MODE_VIBRATE
+                        ? DRAWER_ANIMATION_DURATION_SHORT
+                        : DRAWER_ANIMATION_DURATION)
+                .setStartDelay(mState.ringerModeInternal == RINGER_MODE_VIBRATE
+                        ? DRAWER_ANIMATION_DURATION - DRAWER_ANIMATION_DURATION_SHORT
+                        : 0)
+                .alpha(1f)
+                .translationX(0f)
+                .translationY(0f)
+                .start();
+
+        // Animate the selected ringer view up to that ringer's position in the drawer.
+        mSelectedRingerContainer.animate()
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setDuration(DRAWER_ANIMATION_DURATION)
+                .withEndAction(() ->
+                        getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(VISIBLE));
+
+        if (!isLandscape()) {
+            mSelectedRingerContainer.animate()
+                    .translationY(getTranslationInDrawerForRingerMode(mState.ringerModeInternal))
+                    .start();
+        } else {
+            mSelectedRingerContainer.animate()
+                    .translationX(getTranslationInDrawerForRingerMode(mState.ringerModeInternal))
+                    .start();
+        }
+
+        mIsRingerDrawerOpen = true;
+    }
+
+    /** Animates away the ringer drawer. */
+    private void hideRingerDrawer() {
+        // Hide the drawer icon for the selected ringer - it's visible in the ringer button and we
+        // don't want to be able to see it while it animates away.
+        getDrawerIconViewForMode(mState.ringerModeInternal).setVisibility(INVISIBLE);
+
+        mRingerDrawerContainer.animate()
+                .alpha(0f)
+                .setDuration(DRAWER_ANIMATION_DURATION)
+                .setStartDelay(0)
+                .withEndAction(() -> mRingerDrawerContainer.setVisibility(INVISIBLE));
+
+        if (!isLandscape()) {
+            mRingerDrawerContainer.animate()
+                    .translationY(mRingerDrawerItemSize * 2)
+                    .start();
+        } else {
+            mRingerDrawerContainer.animate()
+                    .translationX(mRingerDrawerItemSize * 2)
+                    .start();
+        }
+
+        mSelectedRingerContainer.animate()
+                .translationX(0f)
+                .translationY(0f)
+                .start();
+
+        mIsRingerDrawerOpen = false;
+    }
+
     public void initSettingsH() {
         if (mSettingsView != null) {
             mSettingsView.setVisibility(
@@ -555,12 +852,8 @@
                         mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                     }
                 }
-                Events.writeEvent(Events.EVENT_RINGER_TOGGLE, newRingerMode);
-                incrementManualToggleCount();
-                updateRingerH();
-                provideTouchFeedbackH(newRingerMode);
-                mController.setRingerMode(newRingerMode, false);
-                maybeShowToastH(newRingerMode);
+
+                setRingerMode(newRingerMode);
             });
         }
         updateRingerH();
@@ -809,6 +1102,8 @@
                     mDialog.dismiss();
                     tryToRemoveCaptionsTooltip();
                     mIsAnimatingDismiss = false;
+
+                    hideRingerDrawer();
                 }, 50));
         if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2.0f);
         animator.start();
@@ -889,12 +1184,14 @@
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
+                    mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
                     addAccessibilityDescription(mRingerIcon, RINGER_MODE_VIBRATE,
                             mContext.getString(R.string.volume_ringer_hint_mute));
                     mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
                     break;
                 case AudioManager.RINGER_MODE_SILENT:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                    mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                     mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
                             mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -904,11 +1201,13 @@
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
                     if (!isZenMuted && muted) {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                         addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                 mContext.getString(R.string.volume_ringer_hint_unmute));
                         mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     } else {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+                        mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
                         if (mController.hasVibrator()) {
                             addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
                                     mContext.getString(R.string.volume_ringer_hint_vibrate));
@@ -1075,8 +1374,6 @@
 
         // update icon
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
-        row.icon.setEnabled(iconEnabled);
-        row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
         final int iconRes;
         if (isRingVibrate) {
             iconRes = R.drawable.ic_volume_ringer_vibrate;
@@ -1092,7 +1389,7 @@
                       ? R.drawable.ic_volume_media_low : row.iconRes;
         }
 
-        row.icon.setImageResource(iconRes);
+        row.setIcon(iconRes, mContext.getTheme());
         row.iconState =
                 iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
                 : (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
@@ -1101,18 +1398,35 @@
                         || iconRes == R.drawable.ic_volume_media_low)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
-        if (iconEnabled) {
-            if (isRingStream) {
-                if (isRingVibrate) {
-                    row.icon.setContentDescription(mContext.getString(
-                            R.string.volume_stream_content_description_unmute,
-                            getStreamLabelH(ss)));
-                } else {
-                    if (mController.hasVibrator()) {
+
+        if (row.icon != null) {
+            if (iconEnabled) {
+                if (isRingStream) {
+                    if (isRingVibrate) {
                         row.icon.setContentDescription(mContext.getString(
-                                mShowA11yStream
-                                        ? R.string.volume_stream_content_description_vibrate_a11y
-                                        : R.string.volume_stream_content_description_vibrate,
+                                R.string.volume_stream_content_description_unmute,
+                                getStreamLabelH(ss)));
+                    } else {
+                        if (mController.hasVibrator()) {
+                            row.icon.setContentDescription(mContext.getString(
+                                    mShowA11yStream
+                                            ? R.string.volume_stream_content_description_vibrate_a11y
+                                            : R.string.volume_stream_content_description_vibrate,
+                                    getStreamLabelH(ss)));
+                        } else {
+                            row.icon.setContentDescription(mContext.getString(
+                                    mShowA11yStream
+                                            ? R.string.volume_stream_content_description_mute_a11y
+                                            : R.string.volume_stream_content_description_mute,
+                                    getStreamLabelH(ss)));
+                        }
+                    }
+                } else if (isA11yStream) {
+                    row.icon.setContentDescription(getStreamLabelH(ss));
+                } else {
+                    if (ss.muted || mAutomute && ss.level == 0) {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_unmute,
                                 getStreamLabelH(ss)));
                     } else {
                         row.icon.setContentDescription(mContext.getString(
@@ -1122,23 +1436,9 @@
                                 getStreamLabelH(ss)));
                     }
                 }
-            } else if (isA11yStream) {
-                row.icon.setContentDescription(getStreamLabelH(ss));
             } else {
-                if (ss.muted || mAutomute && ss.level == 0) {
-                   row.icon.setContentDescription(mContext.getString(
-                           R.string.volume_stream_content_description_unmute,
-                           getStreamLabelH(ss)));
-                } else {
-                    row.icon.setContentDescription(mContext.getString(
-                            mShowA11yStream
-                                    ? R.string.volume_stream_content_description_mute_a11y
-                                    : R.string.volume_stream_content_description_mute,
-                            getStreamLabelH(ss)));
-                }
+                row.icon.setContentDescription(getStreamLabelH(ss));
             }
-        } else {
-            row.icon.setContentDescription(getStreamLabelH(ss));
         }
 
         // ensure tracking is disabled if zenMuted
@@ -1167,22 +1467,29 @@
         if (!useActiveColoring && !mChangeVolumeRowTintWhenInactive) {
             return;
         }
-        final ColorStateList tint = useActiveColoring
+        final ColorStateList colorTint = useActiveColoring
                 ? Utils.getColorAccent(mContext)
                 : Utils.getColorAttr(mContext, android.R.attr.colorForeground);
         final int alpha = useActiveColoring
-                ? Color.alpha(tint.getDefaultColor())
+                ? Color.alpha(colorTint.getDefaultColor())
                 : getAlphaAttr(android.R.attr.secondaryContentAlpha);
-        if (tint == row.cachedTint) return;
-        row.slider.setProgressTintList(tint);
-        row.slider.setThumbTintList(tint);
-        row.slider.setProgressBackgroundTintList(tint);
-        row.slider.setAlpha(((float) alpha) / 255);
-        row.icon.setImageTintList(tint);
-        row.icon.setImageAlpha(alpha);
-        row.cachedTint = tint;
+
+        final ColorStateList bgTint = Utils.getColorAttr(
+                mContext, android.R.attr.colorBackgroundFloating);
+
+        row.sliderProgressSolid.setTintList(colorTint);
+        row.sliderBgIcon.setTintList(colorTint);
+
+        row.sliderBgSolid.setTintList(bgTint);
+        row.sliderProgressIcon.setTintList(bgTint);
+
+        if (row.icon != null) {
+            row.icon.setImageTintList(colorTint);
+            row.icon.setImageAlpha(alpha);
+        }
+
         if (row.number != null) {
-            row.number.setTextColor(tint);
+            row.number.setTextColor(colorTint);
             row.number.setAlpha(alpha);
         }
     }
@@ -1538,6 +1845,10 @@
         private View view;
         private TextView header;
         private ImageButton icon;
+        private Drawable sliderBgSolid;
+        private AlphaTintDrawableWrapper sliderBgIcon;
+        private Drawable sliderProgressSolid;
+        private AlphaTintDrawableWrapper sliderProgressIcon;
         private SeekBar slider;
         private TextView number;
         private int stream;
@@ -1555,5 +1866,69 @@
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
         private FrameLayout dndIcon;
+
+        void setIcon(int iconRes, Resources.Theme theme) {
+            if (icon != null) {
+                icon.setImageResource(iconRes);
+            }
+
+            sliderProgressIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
+            sliderBgIcon.setDrawable(view.getResources().getDrawable(iconRes, theme));
+        }
+    }
+
+    /**
+     * Click listener added to each ringer option in the drawer. This will initiate the animation to
+     * select and then close the ringer drawer, and actually change the ringer mode.
+     */
+    private class RingerDrawerItemClickListener implements View.OnClickListener {
+        private final int mClickedRingerMode;
+
+        RingerDrawerItemClickListener(int clickedRingerMode) {
+            mClickedRingerMode = clickedRingerMode;
+        }
+
+        @Override
+        public void onClick(View view) {
+            setRingerMode(mClickedRingerMode);
+
+            mRingerDrawerIconAnimatingSelected = getDrawerIconViewForMode(mClickedRingerMode);
+            mRingerDrawerIconAnimatingDeselected = getDrawerIconViewForMode(
+                    mState.ringerModeInternal);
+
+            // Begin switching the selected icon and deselected icon colors since the background is
+            // going to animate behind the new selection.
+            mRingerDrawerIconColorAnimator.start();
+
+            mSelectedRingerContainer.setVisibility(View.INVISIBLE);
+            mRingerDrawerNewSelectionBg.setAlpha(1f);
+            mRingerDrawerNewSelectionBg.animate()
+                    .setInterpolator(Interpolators.ACCELERATE_DECELERATE)
+                    .setDuration(DRAWER_ANIMATION_DURATION_SHORT)
+                    .withEndAction(() -> {
+                        mRingerDrawerNewSelectionBg.setAlpha(0f);
+
+                        if (!isLandscape()) {
+                            mSelectedRingerContainer.setTranslationY(
+                                    getTranslationInDrawerForRingerMode(mClickedRingerMode));
+                        } else {
+                            mSelectedRingerContainer.setTranslationX(
+                                    getTranslationInDrawerForRingerMode(mClickedRingerMode));
+                        }
+
+                        mSelectedRingerContainer.setVisibility(VISIBLE);
+                        hideRingerDrawer();
+                    });
+
+            if (!isLandscape()) {
+                mRingerDrawerNewSelectionBg.animate()
+                        .translationY(getTranslationInDrawerForRingerMode(mClickedRingerMode))
+                        .start();
+            } else {
+                mRingerDrawerNewSelectionBg.animate()
+                        .translationX(getTranslationInDrawerForRingerMode(mClickedRingerMode))
+                        .start();
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 3e873d1..700f101 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -150,7 +150,10 @@
     }
 
     @Test
-    public void dozeTimeTick() {
+    public void dozeTimeTick() throws RemoteException {
+        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD);
+        mFgExecutor.runAllReady();
         mUdfpsController.dozeTimeTick();
         verify(mUdfpsView).dozeTimeTick();
     }
@@ -240,7 +243,8 @@
 
     @Test
     public void registersViewForCallbacks() throws RemoteException {
-        verify(mStatusBarStateController).addCallback(mUdfpsView);
-        verify(mStatusBar).addExpansionChangedListener(mUdfpsView);
+        verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
+        verify(mStatusBar).addExpansionChangedListener(
+                mUdfpsController.mStatusBarExpansionListener);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
index 5c179d4..c8f223b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
@@ -69,7 +69,7 @@
 
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 12 /* x */, 5 /* y */, 0);
@@ -86,7 +86,7 @@
 
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 12 /* x */, 5 /* y */, 0);
@@ -105,7 +105,7 @@
 
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         // Would go to left view if attached, but goes to right instead as left should be detached.
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
@@ -122,7 +122,7 @@
 
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 12 /* x */, 5 /* y */, 0);
@@ -138,7 +138,7 @@
 
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 18 /* x */, 5 /* y */, 0);
@@ -154,7 +154,7 @@
         mNearestTouchFrame.setIsVertical(true);
         mNearestTouchFrame.addView(top);
         mNearestTouchFrame.addView(bottom);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 5 /* x */, 12 /* y */, 0);
@@ -170,7 +170,7 @@
         mNearestTouchFrame.setIsVertical(true);
         mNearestTouchFrame.addView(top);
         mNearestTouchFrame.addView(bottom);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0,
                 5 /* x */, 18 /* y */, 0);
@@ -184,7 +184,7 @@
         View view = mockViewAt(0, 20, 10, 10);
         when(view.isAttachedToWindow()).thenReturn(false);
         mNearestTouchFrame.addView(view);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0);
         mNearestTouchFrame.onTouchEvent(ev);
@@ -201,7 +201,7 @@
         mNearestTouchFrame.addView(view1);
         mNearestTouchFrame.addView(view2);
         mNearestTouchFrame.addView(view3);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 30, 30);
 
         MotionEvent ev = MotionEvent.obtain(0, 0, 0, 5 /* x */, 18 /* y */, 0);
         mNearestTouchFrame.onTouchEvent(ev);
@@ -213,11 +213,9 @@
     public void testCachedRegionsSplit_horizontal() {
         View left = mockViewAt(0, 0, 5, 20);
         View right = mockViewAt(15, 0, 5, 20);
-        mNearestTouchFrame.layout(0, 0, 20, 20);
-
         mNearestTouchFrame.addView(left);
         mNearestTouchFrame.addView(right);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.layout(0, 0, 20, 20);
 
         Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
         assertEquals(2, childRegions.size());
@@ -231,12 +229,10 @@
     public void testCachedRegionsSplit_vertical() {
         View top = mockViewAt(0, 0, 20, 5);
         View bottom = mockViewAt(0, 15, 20, 5);
-        mNearestTouchFrame.layout(0, 0, 20, 20);
-        mNearestTouchFrame.setIsVertical(true);
-
         mNearestTouchFrame.addView(top);
         mNearestTouchFrame.addView(bottom);
-        mNearestTouchFrame.onMeasure(0, 0);
+        mNearestTouchFrame.setIsVertical(true);
+        mNearestTouchFrame.layout(0, 0, 20, 20);
 
         Map<View, Rect> childRegions = mNearestTouchFrame.getFullTouchableChildRegions();
         assertEquals(2, childRegions.size());
@@ -256,6 +252,8 @@
         }).when(v).getLocationInWindow(any());
         when(v.isClickable()).thenReturn(true);
         when(v.isAttachedToWindow()).thenReturn(true);
+        when(v.getWidth()).thenReturn(width);
+        when(v.getHeight()).thenReturn(height);
 
         // Stupid final methods.
         v.setLeft(0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index c8e9396..2a4b41c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -464,7 +464,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromNotification(mContext, tile, sbn);
@@ -482,7 +482,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromNotification(mContext, tile, sbn);
@@ -496,7 +496,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
@@ -511,7 +511,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_4, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile actual = PeopleSpaceUtils
                 .augmentTileFromVisibleNotifications(mContext, tile,
@@ -526,7 +526,7 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
                 .augmentTilesFromVisibleNotifications(
@@ -545,13 +545,13 @@
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(1)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         PeopleSpaceTile tile2 =
                 new PeopleSpaceTile
                         .Builder(SHORTCUT_ID_2, "userName2", ICON, new Intent())
                         .setPackageName(PACKAGE_NAME)
-                        .setUid(0)
+                        .setUserHandle(new UserHandle(0))
                         .build();
         List<PeopleSpaceTile> actualList = PeopleSpaceUtils
                 .augmentTilesFromVisibleNotifications(mContext, List.of(tile1, tile2),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 1c8324c..800d859 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -109,7 +109,7 @@
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
                     .setPackageName(TEST_PACKAGE_A)
-                    .setUid(0)
+                    .setUserHandle(new UserHandle(1))
                     .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 2dfd388..d40e6a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -89,6 +89,8 @@
     private View mEdit;
     @Mock
     private MultiUserSwitch mMultiUserSwitch;
+    @Mock
+    private View mPowerMenuLiteView;
 
     private QSFooterViewController mController;
 
@@ -111,11 +113,12 @@
         when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
         when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
         when(mView.findViewById(R.id.multi_user_switch)).thenReturn(mMultiUserSwitch);
+        when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView);
 
         mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
                 mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
                 new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService,
-                mMetricsLogger);
+                mMetricsLogger, false);
 
         mController.init();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 1ec1da4..5a1bd5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -36,6 +36,7 @@
 import com.android.keyguard.CarrierTextController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 import com.android.systemui.utils.os.FakeHandler;
 
@@ -215,10 +216,11 @@
 
     @Test // throws no Exception
     public void testSetMobileDataIndicators_invalidSim() {
-        mSignalCallback.setMobileDataIndicators(
+        MobileDataIndicators indicators = new MobileDataIndicators(
                 mock(NetworkController.IconState.class),
                 mock(NetworkController.IconState.class),
                 0, 0, true, true, "", "", "", true, 0, true, true);
+        mSignalCallback.setMobileDataIndicators(indicators);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index faf43a2..1c29a81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -134,9 +135,11 @@
     public void testStateUnavailable_wifiDisabled() {
         NetworkController.IconState qsIcon =
                 new NetworkController.IconState(false, 0, "");
-        mSignalCallback.setWifiIndicators(false, mock(NetworkController.IconState.class),
+        WifiIndicators indicators = new WifiIndicators(
+                false, mock(NetworkController.IconState.class),
                 qsIcon, false,false, "",
                 false, "");
+        mSignalCallback.setWifiIndicators(indicators);
         mTestableLooper.processAllMessages();
 
         assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
@@ -146,9 +149,11 @@
     public void testStateUnavailable_wifiNotConnected() {
         NetworkController.IconState qsIcon =
                 new NetworkController.IconState(false, 0, "");
-        mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
+        WifiIndicators indicators = new WifiIndicators(
+                true, mock(NetworkController.IconState.class),
                 qsIcon, false,false, "",
                 false, "");
+        mSignalCallback.setWifiIndicators(indicators);
         mTestableLooper.processAllMessages();
 
         assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
@@ -157,9 +162,11 @@
     private void enableWifiAndProcessMessages() {
         NetworkController.IconState qsIcon =
                 new NetworkController.IconState(true, 0, "");
-        mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
+        WifiIndicators indicators = new WifiIndicators(
+                true, mock(NetworkController.IconState.class),
                 qsIcon, false,false, "",
                 false, "");
+        mSignalCallback.setWifiIndicators(indicators);
         mTestableLooper.processAllMessages();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
index a75c39c..9e62a62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
@@ -24,6 +24,7 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IScrollCaptureConnection;
@@ -46,7 +47,7 @@
     }
 
     @Override
-    public void startCapture(Surface surface) {
+    public ICancellationSignal startCapture(Surface surface) {
         mSurface = surface;
         mHwuiContext = new HwuiContext(false, surface);
         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -56,27 +57,28 @@
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+        return null;
     }
 
     @Override
-    public void requestImage(Rect rect) {
+    public ICancellationSignal requestImage(Rect rect) {
         Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height());
         mPaint.setColor(mColors[mNextColor]);
         canvas.drawRect(rect, mPaint);
         mNextColor = (mNextColor++) % mColors.length;
-        long frameNumber = mSurface.getNextFrameNumber();
         mHwuiContext.unlockAndPost(canvas);
         try {
-            mCallbacks.onCaptureBufferSent(frameNumber, rect);
+            mCallbacks.onImageRequestCompleted(0, rect);
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         }
+        return null;
     }
 
     @Override
-    public void endCapture() {
+    public ICancellationSignal endCapture() {
         try {
-            mCallbacks.onConnectionClosed();
+            mCallbacks.onCaptureEnded();
         } catch (RemoteException e) {
             e.rethrowAsRuntimeException();
         } finally {
@@ -84,6 +86,12 @@
             mSurface = null;
             mCallbacks = null;
         }
+        return null;
+    }
+
+    @Override
+    public void close() throws RemoteException {
+
     }
 
     // From android.view.Surface, but issues render requests synchronously with waitForPresent(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 580f800..802b462 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -29,7 +29,6 @@
 import static java.util.Objects.requireNonNull;
 
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
@@ -37,6 +36,7 @@
 import android.view.Display;
 import android.view.IScrollCaptureCallbacks;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -82,10 +82,11 @@
     public void testBasicClientFlow() throws RemoteException {
         doAnswer((Answer<Void>) invocation -> {
             IScrollCaptureCallbacks cb = invocation.getArgument(3);
-            cb.onConnected(
-                    new FakeScrollCaptureConnection(cb),
-                    /* scrollBounds */ new Rect(0, 0, 100, 100),
-                    /* positionInWindow */ new Point(0, 0));
+            cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
+                    .setBoundsInWindow(new Rect(0, 0, 100, 100))
+                    .setWindowBounds(new Rect(0, 0, 100, 100))
+                    .setConnection(new FakeScrollCaptureConnection(cb))
+                    .build());
             return null;
         }).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */  isNull(),
                 /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
index 2b3ca7c..6564d58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
@@ -19,15 +19,14 @@
 import static org.junit.Assert.fail;
 
 import android.content.Intent;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
 import android.util.Log;
 import android.view.Display;
 import android.view.IScrollCaptureCallbacks;
-import android.view.IScrollCaptureConnection;
 import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
 import android.view.WindowManagerGlobal;
 
 import androidx.test.filters.SmallTest;
@@ -67,31 +66,27 @@
             wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
                     new IScrollCaptureCallbacks.Stub() {
                         @Override
-                        public void onConnected(
-                                IScrollCaptureConnection connection, Rect scrollBounds,
-                                Point positionInWindow) {
-                            Log.d(TAG,
-                                    "client connected: " + connection + "[scrollBounds= "
-                                            + scrollBounds + ", "
-                                            + "positionInWindow=" + positionInWindow + "]");
+                        public void onScrollCaptureResponse(ScrollCaptureResponse response)
+                                throws RemoteException {
+                            Log.d(TAG, "onScrollCaptureResponse: " + response);
                             latch.countDown();
                         }
 
                         @Override
-                        public void onUnavailable() {
-                        }
-
-                        @Override
                         public void onCaptureStarted() {
                         }
 
                         @Override
-                        public void onCaptureBufferSent(long frameNumber, Rect capturedArea) {
+                        public void onImageRequestCompleted(int i, Rect rect)
+                                throws RemoteException {
+
                         }
 
                         @Override
-                        public void onConnectionClosed() {
+                        public void onCaptureEnded() throws RemoteException {
+
                         }
+
                     });
         } catch (RemoteException e) {
             Log.e(TAG, "request failed", e);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index ce14bca..7ff056e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
 
 import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
@@ -40,6 +42,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.UserInfo;
@@ -94,8 +97,12 @@
 
     private static final String ORGANIZATION_NAME = "organization";
 
+    private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
+            "bar");
+
     private String mDisclosureWithOrganization;
     private String mDisclosureGeneric;
+    private String mFinancedDisclosureWithOrganization;
 
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
@@ -156,6 +163,8 @@
         mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name,
                 ORGANIZATION_NAME);
         mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic);
+        mFinancedDisclosureWithOrganization = mContext.getString(
+                R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME);
 
         when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
         when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
@@ -165,6 +174,11 @@
                 .thenReturn(mIndicationAreaBottom);
         when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
 
+        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+                .thenReturn(DEVICE_OWNER_COMPONENT);
+        when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+                .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
+
         mWakeLock = new WakeLockFake();
         mWakeLockBuilder = new WakeLockFake.Builder(mContext);
         mWakeLockBuilder.setWakeLock(mWakeLock);
@@ -372,6 +386,22 @@
     }
 
     @Test
+    public void disclosure_deviceOwner_financedDeviceWithOrganizationName() {
+        createController();
+
+        when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+        when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(ORGANIZATION_NAME);
+        when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+                .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+        sendUpdateDisclosureBroadcast();
+
+        verify(mRotateTextViewController).updateIndication(eq(INDICATION_TYPE_DISCLOSURE),
+                mKeyguardIndicationCaptor.capture(), eq(false));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage())
+                .isEqualTo(mFinancedDisclosureWithOrganization);
+    }
+
+    @Test
     public void transientIndication_holdsWakeLock_whenDozing() {
         createController();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 67c1a08..2418243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -30,7 +30,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 import com.android.systemui.tests.R;
 
 import org.junit.Before;
@@ -86,28 +88,24 @@
         boolean out = true;
         String description = "Test";
         String secondaryLabel = "Secondary label";
-        mHandler.setWifiIndicators(enabled, status, qs, in, out, description, true, secondaryLabel);
+        WifiIndicators indicators = new WifiIndicators(
+                enabled, status, qs, in, out, description, true, secondaryLabel);
+        mHandler.setWifiIndicators(indicators);
         waitForCallbacks();
 
-        ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
-        ArgumentCaptor<Boolean> isTransient = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<String> secondary = ArgumentCaptor.forClass(String.class);
-        Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(),
-                statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(),
-                descArg.capture(), isTransient.capture(), secondary.capture());
-        assertEquals(enabled, (boolean) enableArg.getValue());
-        assertEquals(status, statusArg.getValue());
-        assertEquals(qs, qsArg.getValue());
-        assertEquals(in, (boolean) inArg.getValue());
-        assertEquals(out, (boolean) outArg.getValue());
-        assertEquals(description, descArg.getValue());
-        assertTrue(isTransient.getValue());
-        assertEquals(secondaryLabel, secondary.getValue());
+        ArgumentCaptor<WifiIndicators> indicatorArg =
+                ArgumentCaptor.forClass(WifiIndicators.class);
+        Mockito.verify(mSignalCallback).setWifiIndicators(indicatorArg.capture());
+        WifiIndicators expected = indicatorArg.getValue();
+
+        assertEquals(enabled, expected.enabled);
+        assertEquals(status, expected.statusIcon);
+        assertEquals(qs, expected.qsIcon);
+        assertEquals(in, expected.activityIn);
+        assertEquals(out, expected.activityOut);
+        assertEquals(description, expected.description);
+        assertTrue(expected.isTransient);
+        assertEquals(secondaryLabel, expected.statusLabel);
     }
 
     @Test
@@ -124,37 +122,30 @@
         boolean wide = true;
         int subId = 5;
         boolean roaming = true;
-        mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
+        MobileDataIndicators indicators = new MobileDataIndicators(
+                status, qs, type, qsType, in, out, typeDescription,
                 typeDescriptionHtml, description, wide, subId, roaming, true);
+        mHandler.setMobileDataIndicators(indicators);
         waitForCallbacks();
 
-        ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
-        ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
-        ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<CharSequence> typeContentArg = ArgumentCaptor.forClass(CharSequence.class);
-        ArgumentCaptor<CharSequence> typeContentHtmlArg =
-                ArgumentCaptor.forClass(CharSequence.class);
-        ArgumentCaptor<CharSequence> descArg = ArgumentCaptor.forClass(CharSequence.class);
-        ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class);
-        Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
-                qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
-                outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(),
-                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true));
-        assertEquals(status, statusArg.getValue());
-        assertEquals(qs, qsArg.getValue());
-        assertEquals(type, (int) typeIconArg.getValue());
-        assertEquals(qsType, (int) qsTypeIconArg.getValue());
-        assertEquals(in, (boolean) inArg.getValue());
-        assertEquals(out, (boolean) outArg.getValue());
-        assertEquals(typeDescription, typeContentArg.getValue());
-        assertEquals(typeDescriptionHtml, typeContentHtmlArg.getValue());
-        assertEquals(description, descArg.getValue());
-        assertEquals(wide, (boolean) wideArg.getValue());
-        assertEquals(subId, (int) subIdArg.getValue());
+        ArgumentCaptor<MobileDataIndicators> indicatorArg =
+                ArgumentCaptor.forClass(MobileDataIndicators.class);
+        Mockito.verify(mSignalCallback).setMobileDataIndicators(indicatorArg.capture());
+        MobileDataIndicators expected = indicatorArg.getValue();
+
+        assertEquals(status, expected.statusIcon);
+        assertEquals(qs, expected.qsIcon);
+        assertEquals(type, expected.statusType);
+        assertEquals(qsType, expected.qsType);
+        assertEquals(in, expected.activityIn);
+        assertEquals(out, expected.activityOut);
+        assertEquals(typeDescription, expected.typeContentDescription);
+        assertEquals(typeDescriptionHtml, expected.typeContentDescriptionHtml);
+        assertEquals(description, expected.description);
+        assertEquals(wide, expected.isWide);
+        assertEquals(subId, expected.subId);
+        assertTrue(expected.roaming);
+        assertTrue(expected.showTriangle);
     }
 
     @SuppressWarnings("unchecked")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 89cc2b5..e52b926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -21,9 +21,9 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
@@ -76,6 +76,7 @@
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
 import org.junit.After;
@@ -173,8 +174,6 @@
         mMockSubDefaults = mock(SubscriptionDefaults.class);
         mNetCapabilities = new NetworkCapabilities();
         when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
-        when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
-                new NetworkCapabilities[] { mNetCapabilities });
         when(mMockTm.createForSubscriptionId(anyInt())).thenReturn(mMockTm);
         doAnswer(invocation -> {
             int rssi = invocation.getArgument(0);
@@ -257,8 +256,11 @@
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
         verify(mMockCm, atLeastOnce())
             .registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
-        mDefaultCallbackInWifiTracker = callbackArg.getAllValues().get(0);
-        mDefaultCallbackInNetworkController = callbackArg.getAllValues().get(1);
+        int captureSize = callbackArg.getAllValues().size();
+        assertTrue(captureSize > 1);
+        assertEquals(captureSize % 2, 0);
+        mDefaultCallbackInWifiTracker = callbackArg.getAllValues().get(captureSize - 2);
+        mDefaultCallbackInNetworkController = callbackArg.getAllValues().get(captureSize - 1);
         assertNotNull(mDefaultCallbackInWifiTracker);
         assertNotNull(mDefaultCallbackInNetworkController);
         verify(mMockCm, atLeastOnce()).registerNetworkCallback(
@@ -307,14 +309,18 @@
         setLevel(DEFAULT_LEVEL);
         updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
                 TelephonyManager.NETWORK_TYPE_UMTS);
+        setConnectivityViaCallbackInNetworkController(
+                NetworkCapabilities.TRANSPORT_CELLULAR, true, true, null);
         setConnectivityViaBroadcast(
-            NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
+                NetworkCapabilities.TRANSPORT_CELLULAR, true, true);
     }
 
     public void setConnectivityViaBroadcastForVcn(
             int networkType, boolean validated, boolean isConnected, VcnTransportInfo info) {
         mNetCapabilities.setTransportInfo(info);
         setConnectivityCommon(networkType, validated, isConnected);
+        mDefaultCallbackInNetworkController.onCapabilitiesChanged(
+                mock(Network.class), new NetworkCapabilities(mNetCapabilities));
         Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION);
         mNetworkController.onReceive(mContext, i);
     }
@@ -322,6 +328,8 @@
     public void setConnectivityViaBroadcast(
         int networkType, boolean validated, boolean isConnected) {
         setConnectivityCommon(networkType, validated, isConnected);
+        mDefaultCallbackInNetworkController.onCapabilitiesChanged(
+                mock(Network.class), new NetworkCapabilities(mNetCapabilities));
         Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION);
         mNetworkController.onReceive(mContext, i);
     }
@@ -487,28 +495,21 @@
 
     protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon,
             boolean dataIn, boolean dataOut) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
-        ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<MobileDataIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(MobileDataIndicators.class);
 
         verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
-                    any(),
-                    iconArg.capture(),
-                    anyInt(),
-                    typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
-                    any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
-                    anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
-        IconState iconState = iconArg.getValue();
+                    indicatorsArg.capture());
+        MobileDataIndicators expected = indicatorsArg.getValue();
         int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
                 false);
-        assertEquals("Visibility in, quick settings", visible, iconState.visible);
-        assertEquals("Signal icon in, quick settings", state, iconState.icon);
-        assertEquals("Data icon in, quick settings", typeIcon, (int) typeIconArg.getValue());
+        assertEquals("Visibility in, quick settings", visible, expected.qsIcon.visible);
+        assertEquals("Signal icon in, quick settings", state, expected.qsIcon.icon);
+        assertEquals("Data icon in, quick settings", typeIcon, expected.qsType);
         assertEquals("Data direction in, in quick settings", dataIn,
-                (boolean) dataInArg.getValue());
+                expected.activityIn);
         assertEquals("Data direction out, in quick settings", dataOut,
-                (boolean) dataOutArg.getValue());
+                expected.activityOut);
     }
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon) {
@@ -522,44 +523,35 @@
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
         boolean roaming, boolean inet) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<MobileDataIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(MobileDataIndicators.class);
 
         // TODO: Verify all fields.
         verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
-                iconArg.capture(),
-                any(),
-                typeIconArg.capture(),
-                anyInt(), anyBoolean(), anyBoolean(),
-                any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), eq(roaming), anyBoolean());
-        IconState iconState = iconArg.getValue();
+                indicatorsArg.capture());
+        MobileDataIndicators expected = indicatorsArg.getValue();
         int state = icon == -1 ? 0
                 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
                         !inet);
-        assertEquals("Signal icon in status bar", state, iconState.icon);
-        assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
-        assertEquals("Visibility in status bar", visible, iconState.visible);
+        assertEquals("Signal icon in status bar", state, expected.statusIcon.icon);
+        assertEquals("Data icon in status bar", typeIcon, expected.statusType);
+        assertEquals("Visibility in status bar", visible, expected.statusIcon.visible);
     }
 
     protected void verifyLastMobileDataIndicatorsForVcn(boolean visible, int level, int typeIcon,
             boolean inet) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<MobileDataIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(MobileDataIndicators.class);
 
         verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
-                iconArg.capture(),
-                any(),
-                typeIconArg.capture(),
-                anyInt(), anyBoolean(), anyBoolean(),
-                any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
-        IconState iconState = iconArg.getValue();
+                indicatorsArg.capture());
+
+        MobileDataIndicators expected = indicatorsArg.getValue();
         int state = SignalDrawable.getState(
                 level, CellSignalStrength.getNumSignalStrengthLevels(), !inet);
-        assertEquals("Signal icon in status bar", state, iconState.icon);
-        assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
-        assertEquals("Visibility in status bar", visible, iconState.visible);
+        assertEquals("Signal icon in status bar", state, expected.statusIcon.icon);
+        assertEquals("Data icon in status bar", typeIcon, expected.statusType);
+        assertEquals("Visibility in status bar", visible, expected.statusIcon.visible);
     }
 
     protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
@@ -580,6 +572,8 @@
             boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut,
             boolean cutOut, CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml) {
+        ArgumentCaptor<MobileDataIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(MobileDataIndicators.class);
         ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
         ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class);
@@ -592,17 +586,9 @@
                 ArgumentCaptor.forClass(CharSequence.class);
 
         verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
-                iconArg.capture(),
-                qsIconArg.capture(),
-                typeIconArg.capture(),
-                qsTypeIconArg.capture(),
-                dataInArg.capture(),
-                dataOutArg.capture(),
-                typeContentDescriptionArg.capture(),
-                typeContentDescriptionHtmlArg.capture(),
-                any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
+                indicatorsArg.capture());
 
-        IconState iconState = iconArg.getValue();
+        MobileDataIndicators expected = indicatorsArg.getValue();
 
         int numSignalStrengthBins = CellSignalStrength.getNumSignalStrengthLevels();
         if (mMobileSignalController.mInflateSignalStrengths) {
@@ -610,29 +596,28 @@
             icon++;
         }
         int state = SignalDrawable.getState(icon, numSignalStrengthBins, cutOut);
-        assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
-        assertEquals("Signal icon in status bar", state, iconState.icon);
-        assertEquals("Visibility in status bar", visible, iconState.visible);
+        assertEquals("Data icon in status bar", typeIcon, expected.statusType);
+        assertEquals("Signal icon in status bar", state, expected.statusIcon.icon);
+        assertEquals("Visibility in status bar", visible, expected.statusIcon.visible);
 
-        iconState = qsIconArg.getValue();
         if (visible) {
-            assertEquals("Visibility in quick settings", qsVisible, iconState.visible);
-            assertEquals("Signal icon in quick settings", state, iconState.icon);
+            assertEquals("Visibility in quick settings", qsVisible, expected.qsIcon.visible);
+            assertEquals("Signal icon in quick settings", state, expected.qsIcon.icon);
         } else {
-            assertEquals("Cellular is not default", null, iconState);
+            assertEquals("Cellular is not default", null, expected.qsIcon);
         }
-        assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue());
+        assertEquals("Data icon in quick settings", qsTypeIcon, expected.qsType);
         assertEquals("Data direction in in quick settings", dataIn,
-                (boolean) dataInArg.getValue());
+                expected.activityIn);
         assertEquals("Data direction out in quick settings", dataOut,
-                (boolean) dataOutArg.getValue());
+                expected.activityOut);
         if (typeContentDescription != null) { // Only check if it was provided
             assertEquals("Type content description", typeContentDescription,
-                    typeContentDescriptionArg.getValue());
+                    expected.typeContentDescription);
         }
         if (typeContentDescriptionHtml != null) { // Only check if it was provided
             assertEquals("Type content description (html)", typeContentDescriptionHtml,
-                    typeContentDescriptionHtmlArg.getValue());
+                    expected.typeContentDescriptionHtml);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index fc1a08ac..c6812a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -3,7 +3,6 @@
 import static junit.framework.Assert.assertEquals;
 
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -21,7 +20,7 @@
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -316,44 +315,42 @@
     }
 
     protected void verifyLastQsDataDirection(boolean in, boolean out) {
-        ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<WifiIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(WifiIndicators.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any(), anyBoolean(),
-                any());
-        assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue());
-        assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue());
+                indicatorsArg.capture());
+        WifiIndicators expected = indicatorsArg.getValue();
+        assertEquals("WiFi data in, in quick settings", in, expected.activityIn);
+        assertEquals("WiFi data out, in quick settings", out, expected.activityOut);
     }
 
     protected void verifyLastQsWifiIcon(boolean enabled, boolean connected, int icon,
             String description) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
-        ArgumentCaptor<Boolean> enabledArg = ArgumentCaptor.forClass(Boolean.class);
-        ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<WifiIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(WifiIndicators.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                enabledArg.capture(), any(), iconArg.capture(), anyBoolean(),
-                anyBoolean(), descArg.capture(), anyBoolean(), any());
-        IconState iconState = iconArg.getValue();
-        assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
-        assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue());
+                indicatorsArg.capture());
+        WifiIndicators expected = indicatorsArg.getValue();
+        assertEquals("WiFi enabled, in quick settings", enabled, expected.enabled);
+        assertEquals("WiFI desc (ssid), in quick settings", description, expected.description);
         if (enabled && connected) {
-            assertEquals("WiFi connected, in quick settings", connected, iconState.visible);
-            assertEquals("WiFi signal, in quick settings", icon, iconState.icon);
+            assertEquals("WiFi connected, in quick settings", connected, expected.qsIcon.visible);
+            assertEquals("WiFi signal, in quick settings", icon, expected.qsIcon.icon);
         } else {
-            assertEquals("WiFi is not default", null, iconState);
+            assertEquals("WiFi is not default", null, expected.qsIcon);
         }
     }
 
     protected void verifyLastWifiIcon(boolean visible, int icon) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+        ArgumentCaptor<WifiIndicators> indicatorsArg =
+                ArgumentCaptor.forClass(WifiIndicators.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(),
-                any(), anyBoolean(), any());
-        IconState iconState = iconArg.getValue();
-        assertEquals("WiFi visible, in status bar", visible, iconState.visible);
-        assertEquals("WiFi signal, in status bar", icon, iconState.icon);
+                indicatorsArg.capture());
+        WifiIndicators expected = indicatorsArg.getValue();
+        assertEquals("WiFi visible, in status bar", visible, expected.statusIcon.visible);
+        assertEquals("WiFi signal, in status bar", icon, expected.statusIcon.icon);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c085689..3cea175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -22,6 +22,7 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.KeyguardManager;
@@ -37,6 +38,8 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.Prefs;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
@@ -58,6 +61,11 @@
 public class VolumeDialogImplTest extends SysuiTestCase {
 
     VolumeDialogImpl mDialog;
+    View mActiveRinger;
+    View mDrawerContainer;
+    View mDrawerVibrate;
+    View mDrawerMute;
+    View mDrawerNormal;
 
     @Mock
     VolumeDialogController mController;
@@ -80,6 +88,17 @@
         mDialog.init(0, null);
         State state = createShellState();
         mDialog.onStateChangedH(state);
+
+        mActiveRinger = mDialog.getDialogView().findViewById(
+                R.id.volume_new_ringer_active_icon_container);
+        mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container);
+        mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate);
+        mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute);
+        mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal);
+
+        Prefs.putInt(mContext,
+                Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
+                VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
     }
 
     private State createShellState() {
@@ -207,6 +226,48 @@
         verify(mController, never()).vibrate(any());
     }
 
+    @Test
+    public void testSelectVibrateFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerVibrate.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_VIBRATE, false);
+    }
+
+    @Test
+    public void testSelectMuteFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerMute.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_SILENT, false);
+    }
+
+    @Test
+    public void testSelectNormalFromDrawer() {
+        final State initialUnsetState = new State();
+        initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
+        mDialog.onStateChangedH(initialUnsetState);
+
+        mActiveRinger.performClick();
+        mDrawerNormal.performClick();
+
+        // Make sure we've actually changed the ringer mode.
+        verify(mController, times(1)).setRingerMode(
+                AudioManager.RINGER_MODE_NORMAL, false);
+    }
+
 /*
     @Test
     public void testContentDescriptions() {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f4138d1..542d527 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -44,6 +44,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
@@ -91,7 +92,6 @@
 import android.net.IDnsResolver;
 import android.net.INetd;
 import android.net.INetworkActivityListener;
-import android.net.INetworkManagementEventObserver;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
@@ -194,6 +194,7 @@
 import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
@@ -214,7 +215,6 @@
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.QosCallbackTracker;
-import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.utils.PriorityDump;
 
@@ -332,6 +332,7 @@
     private INetworkStatsService mStatsService;
     private NetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
+    private final NetdCallback mNetdCallback;
 
     /**
      * TestNetworkService (lazily) created upon first usage. Locked to prevent creation of multiple
@@ -1204,6 +1205,13 @@
 
         mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNMS, mNetd);
 
+        mNetdCallback = new NetdCallback();
+        try {
+            mNetd.registerUnsolicitedEventListener(mNetdCallback);
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Error registering event listener :" + e);
+        }
+
         mSettingsObserver = new SettingsObserver(mContext, mHandler);
         registerSettingsCallbacks();
 
@@ -1241,6 +1249,7 @@
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1255,6 +1264,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > TYPE_NONE) {
             netCap.addTransportType(transportType);
@@ -8649,6 +8659,14 @@
         notifyDataStallSuspected(p, network.getNetId());
     }
 
+    private class NetdCallback extends BaseNetdUnsolicitedEventListener {
+        @Override
+        public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel,
+                long timestampNs, int uid) {
+            mNetworkActivityTracker.setAndReportNetworkActive(isActive, timerLabel, timestampNs);
+        }
+    }
+
     private final LegacyNetworkActivityTracker mNetworkActivityTracker;
 
     /**
@@ -8659,7 +8677,6 @@
         private static final int NO_UID = -1;
         private final Context mContext;
         private final INetd mNetd;
-        private final INetworkManagementService mNMS;
         private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
                 new RemoteCallbackList<>();
         // Indicate the current system default network activity is active or not.
@@ -8682,41 +8699,27 @@
         LegacyNetworkActivityTracker(@NonNull Context context, @NonNull Handler handler,
                 @NonNull INetworkManagementService nms, @NonNull INetd netd) {
             mContext = context;
-            mNMS = nms;
             mNetd = netd;
             mHandler = handler;
-            try {
-                mNMS.registerObserver(mDataActivityObserver);
-            } catch (RemoteException e) {
-                loge("Error registering observer :" + e);
-            }
         }
 
-        // TODO: Migrate away the dependency with INetworkManagementEventObserver.
-        private final INetworkManagementEventObserver mDataActivityObserver =
-                new BaseNetworkObserver() {
-                    @Override
-                    public void interfaceClassDataActivityChanged(int transportType, boolean active,
-                            long tsNanos, int uid) {
-                        sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active,
-                                tsNanos);
-                        synchronized (mActiveIdleTimers) {
-                            mNetworkActive = active;
-                            // If there are no idle timers, it means that system is not monitoring
-                            // activity, so the system default network for those default network
-                            // unspecified apps is always considered active.
-                            //
-                            // TODO: If the mActiveIdleTimers is empty, netd will actually not send
-                            // any network activity change event. Whenever this event is received,
-                            // the mActiveIdleTimers should be always not empty. The legacy behavior
-                            // is no-op. Remove to refer to mNetworkActive only.
-                            if (mNetworkActive || mActiveIdleTimers.isEmpty()) {
-                                mHandler.sendMessage(
-                                        mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY));
-                            }
-                        }
-                    }
-                };
+        public void setAndReportNetworkActive(boolean active, int transportType, long tsNanos) {
+            sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos);
+            synchronized (mActiveIdleTimers) {
+                mNetworkActive = active;
+                // If there are no idle timers, it means that system is not monitoring
+                // activity, so the system default network for those default network
+                // unspecified apps is always considered active.
+                //
+                // TODO: If the mActiveIdleTimers is empty, netd will actually not send
+                // any network activity change event. Whenever this event is received,
+                // the mActiveIdleTimers should be always not empty. The legacy behavior
+                // is no-op. Remove to refer to mNetworkActive only.
+                if (mNetworkActive || mActiveIdleTimers.isEmpty()) {
+                    mHandler.sendMessage(mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY));
+                }
+            }
+        }
 
         // The network activity should only be updated from ConnectivityService handler thread
         // when mActiveIdleTimers lock is held.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5e61f94..7cd49497 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15127,13 +15127,6 @@
         }
 
         @Override
-        public void setPendingIntentWhitelistDuration(IIntentSender target, IBinder allowlistToken,
-                long duration, int type) {
-            mPendingIntentController.setPendingIntentAllowlistDuration(target, allowlistToken,
-                    duration, type, REASON_UNKNOWN, "");
-        }
-
-        @Override
         public int getPendingIntentFlags(IIntentSender target) {
             return mPendingIntentController.getPendingIntentFlags(target);
         }
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 4061df4..03eddc9 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -44,6 +44,23 @@
           "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
+    },
+    {
+      "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
+      "name": "FrameworksCoreTests",
+      "options": [
+        { "include-filter": "com.android.internal.os.BatteryStatsTests" },
+        { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+      ]
+    },
+    {
+      "file_patterns": ["Battery[^/]*\\.java", "MeasuredEnergy[^/]*\\.java"],
+      "name": "FrameworksServicesTests",
+      "options": [
+        { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
+        { "include-filter": "com.android.server.am.MeasuredEnergySnapshotTest" },
+        { "include-filter": "com.android.server.am.BatteryExternalStatsWorkerTest" }
+      ]
     }
   ],
   "postsubmit": [
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 322c210..2de709e 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -246,8 +246,8 @@
         ClipData primaryClip;
         /** UID that set {@link #primaryClip}. */
         int primaryClipUid = android.os.Process.NOBODY_UID;
-        /** Application label of the app that set {@link #primaryClip}. */
-        CharSequence mPrimaryClipAppLabel;
+        /** Package of the app that set {@link #primaryClip}. */
+        String mPrimaryClipPackage;
 
         final HashSet<String> activePermissionOwners
                 = new HashSet<String>();
@@ -528,20 +528,9 @@
             }
         }
 
-        // Retrieve the app label of the source of the clip data
-        CharSequence sourceAppLabel = null;
-        if (clip != null && sourcePackage != null) {
-            try {
-                sourceAppLabel =
-                        mPm.getApplicationLabel(mPm.getApplicationInfo(sourcePackage, 0));
-            } catch (PackageManager.NameNotFoundException e) {
-                // leave label as null
-            }
-        }
-
         // Update this user
         final int userId = UserHandle.getUserId(uid);
-        setPrimaryClipInternal(getClipboard(userId), clip, uid, sourceAppLabel);
+        setPrimaryClipInternal(getClipboard(userId), clip, uid, sourcePackage);
 
         // Update related users
         List<UserInfo> related = getRelatedProfiles(userId);
@@ -576,7 +565,7 @@
                                 UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id);
                         if (canCopyIntoProfile) {
                             setPrimaryClipInternal(
-                                    getClipboard(id), clip, uid, sourceAppLabel);
+                                    getClipboard(id), clip, uid, sourcePackage);
                         }
                     }
                 }
@@ -590,7 +579,7 @@
     }
 
     private void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
-            int uid, @Nullable CharSequence sourceAppLabel) {
+            int uid, @Nullable String sourcePackage) {
         revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
@@ -599,10 +588,10 @@
         clipboard.primaryClip = clip;
         if (clip != null) {
             clipboard.primaryClipUid = uid;
-            clipboard.mPrimaryClipAppLabel = sourceAppLabel;
+            clipboard.mPrimaryClipPackage = sourcePackage;
         } else {
             clipboard.primaryClipUid = android.os.Process.NOBODY_UID;
-            clipboard.mPrimaryClipAppLabel = null;
+            clipboard.mPrimaryClipPackage = null;
         }
         if (clip != null) {
             final ClipDescription description = clip.getDescription();
@@ -887,12 +876,23 @@
             return;
         }
 
+        // Retrieve the app label of the source of the clip data
+        CharSequence sourceAppLabel = null;
+        if (clipboard.mPrimaryClipPackage != null) {
+            try {
+                sourceAppLabel = mPm.getApplicationLabel(
+                        mPm.getApplicationInfo(clipboard.mPrimaryClipPackage, 0));
+            } catch (PackageManager.NameNotFoundException e) {
+                // leave label as null
+            }
+        }
+
         try {
             CharSequence callingAppLabel = mPm.getApplicationLabel(
                     mPm.getApplicationInfo(callingPackage, 0));
             String message;
-            if (clipboard.mPrimaryClipAppLabel != null) {
-                message = callingAppLabel + " pasted from " + clipboard.mPrimaryClipAppLabel;
+            if (sourceAppLabel != null) {
+                message = callingAppLabel + " pasted from " + sourceAppLabel;
             } else {
                 message = callingAppLabel + " pasted from clipboard";
             }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 2005a74..86142bc 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -469,18 +469,10 @@
 
         DisplayDeviceConfig displayDeviceConfig = logicalDisplay
                 .getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
-        // TODO: (b/178183143) Ensure that the ddc is not null
-        if (displayDeviceConfig != null) {
-            mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease();
-            mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease();
-            mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease();
-            mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease();
-        } else {
-            mBrightnessRampRateFastDecrease = 1.0f;
-            mBrightnessRampRateFastIncrease = 1.0f;
-            mBrightnessRampRateSlowDecrease = 1.0f;
-            mBrightnessRampRateSlowIncrease = 1.0f;
-        }
+        mBrightnessRampRateFastDecrease = displayDeviceConfig.getBrightnessRampFastDecrease();
+        mBrightnessRampRateFastIncrease = displayDeviceConfig.getBrightnessRampFastIncrease();
+        mBrightnessRampRateSlowDecrease = displayDeviceConfig.getBrightnessRampSlowDecrease();
+        mBrightnessRampRateSlowIncrease = displayDeviceConfig.getBrightnessRampSlowIncrease();
         mSkipScreenOnBrightnessRamp = resources.getBoolean(
                 com.android.internal.R.bool.config_skipScreenOnBrightnessRamp);
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 48f335d..3709963 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -638,11 +638,7 @@
                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
                 mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
                 mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
-                if (mDisplayDeviceConfig != null) {
-                    mInfo.brightnessDefault = mDisplayDeviceConfig.getBrightnessDefault();
-                } else {
-                    mInfo.brightnessDefault = 0.5f;
-                }
+                mInfo.brightnessDefault = getDisplayDeviceConfig().getBrightnessDefault();
             }
             return mInfo;
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 1e66589..87e63eb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5414,37 +5414,36 @@
         @BinderThread
         @ShellCommandResult
         private int onCommandWithSystemIdentity(@Nullable String cmd) {
-            if ("get-last-switch-user-id".equals(cmd)) {
-                return mService.getLastSwitchUserId(this);
-            }
-
-            // For existing "adb shell ime <command>".
-            if ("ime".equals(cmd)) {
-                final String imeCommand = getNextArg();
-                if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
-                    onImeCommandHelp();
-                    return ShellCommandResult.SUCCESS;
+            switch (TextUtils.emptyIfNull(cmd)) {
+                case "get-last-switch-user-id":
+                    return mService.getLastSwitchUserId(this);
+                case "ime": {  // For "adb shell ime <command>".
+                    final String imeCommand = TextUtils.emptyIfNull(getNextArg());
+                    switch (imeCommand) {
+                        case "":
+                        case "-h":
+                        case "help":
+                            return onImeCommandHelp();
+                        case "list":
+                            return mService.handleShellCommandListInputMethods(this);
+                        case "enable":
+                            return mService.handleShellCommandEnableDisableInputMethod(this, true);
+                        case "disable":
+                            return mService.handleShellCommandEnableDisableInputMethod(this, false);
+                        case "set":
+                            return mService.handleShellCommandSetInputMethod(this);
+                        case "reset":
+                            return mService.handleShellCommandResetInputMethod(this);
+                        case "tracing":
+                            return mService.handleShellCommandTraceInputMethod(this);
+                        default:
+                            getOutPrintWriter().println("Unknown command: " + imeCommand);
+                            return ShellCommandResult.FAILURE;
+                    }
                 }
-                switch (imeCommand) {
-                    case "list":
-                        return mService.handleShellCommandListInputMethods(this);
-                    case "enable":
-                        return mService.handleShellCommandEnableDisableInputMethod(this, true);
-                    case "disable":
-                        return mService.handleShellCommandEnableDisableInputMethod(this, false);
-                    case "set":
-                        return mService.handleShellCommandSetInputMethod(this);
-                    case "reset":
-                        return mService.handleShellCommandResetInputMethod(this);
-                    case "tracing":
-                        return mService.handleShellCommandTraceInputMethod(this);
-                    default:
-                        getOutPrintWriter().println("Unknown command: " + imeCommand);
-                        return ShellCommandResult.FAILURE;
-                }
+                default:
+                    return handleDefaultCommands(cmd);
             }
-
-            return handleDefaultCommands(cmd);
         }
 
         @BinderThread
@@ -5461,7 +5460,9 @@
             }
         }
 
-        private void onImeCommandHelp() {
+        @BinderThread
+        @ShellCommandResult
+        private int onImeCommandHelp() {
             try (IndentingPrintWriter pw =
                          new IndentingPrintWriter(getOutPrintWriter(), "  ", 100)) {
                 pw.println("ime <command>:");
@@ -5516,6 +5517,7 @@
 
                 pw.decreaseIndent();
             }
+            return ShellCommandResult.SUCCESS;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 57e9fc9..2920ddb 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -52,6 +52,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
 import android.location.IGeocodeListener;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssNmeaListener;
@@ -463,7 +464,7 @@
                     .build();
             final LocationProviderManager manager = getOrAddLocationProviderManager(name);
             manager.setMockProvider(new MockLocationProvider(properties,
-                    CallerIdentity.fromContext(mContext), /*locationTags*/ null));
+                    CallerIdentity.fromContext(mContext), Collections.emptySet()));
         }
     }
 
@@ -930,6 +931,22 @@
     }
 
     @Override
+    public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
+            @Nullable String attributionTag, String listenerId) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, attributionTag,
+                    listenerId);
+        }
+    }
+
+    @Override
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.removeGnssAntennaInfoListener(listener);
+        }
+    }
+
+    @Override
     public void addProviderRequestListener(IProviderRequestListener listener) {
         for (LocationProviderManager manager : mProviderManagers) {
             manager.addProviderRequestListener(listener);
@@ -1153,7 +1170,7 @@
 
     @Override
     public void addTestProvider(String provider, ProviderProperties properties,
-            List<String> locationTags, String packageName, String attributionTag) {
+            List<String> extraAttributionTags, String packageName, String attributionTag) {
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
         if (!mInjector.getAppOpsHelper().noteOp(AppOpsManager.OP_MOCK_LOCATION, identity)) {
@@ -1162,7 +1179,7 @@
 
         final LocationProviderManager manager = getOrAddLocationProviderManager(provider);
         manager.setMockProvider(new MockLocationProvider(properties, identity,
-                (locationTags != null) ? new ArraySet<>(locationTags) : null));
+                new ArraySet<>(extraAttributionTags)));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 21a9b04..5dc3ed8 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -26,6 +26,8 @@
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -50,15 +52,11 @@
 
         switch (cmd) {
             case "is-location-enabled": {
-                int userId = parseUserId();
-                boolean enabled = mService.isLocationEnabledForUser(userId);
-                getOutPrintWriter().println(enabled);
+                handleIsLocationEnabled();
                 return 0;
             }
             case "set-location-enabled": {
-                int userId = parseUserId();
-                boolean enabled = Boolean.parseBoolean(getNextArgRequired());
-                mService.setLocationEnabledForUser(enabled, userId);
+                handleSetLocationEnabled();
                 return 0;
             }
             case "providers": {
@@ -73,36 +71,23 @@
     private int parseProvidersCommand(String cmd) {
         switch (cmd) {
             case "add-test-provider": {
-                String provider = getNextArgRequired();
-                ProviderProperties properties = parseTestProviderProviderProperties();
-                mService.addTestProvider(provider, properties, /*locationTags*/ null,
-                        mContext.getOpPackageName(), mContext.getFeatureId());
+                handleAddTestProvider();
                 return 0;
             }
             case "remove-test-provider": {
-                String provider = getNextArgRequired();
-                mService.removeTestProvider(provider, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                handleRemoveTestProvider();
                 return 0;
             }
             case "set-test-provider-enabled": {
-                String provider = getNextArgRequired();
-                boolean enabled = Boolean.parseBoolean(getNextArgRequired());
-                mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                handleSetTestProviderEnabled();
                 return 0;
             }
             case "set-test-provider-location": {
-                String provider = getNextArgRequired();
-                Location location = parseTestProviderLocation(provider);
-                mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
-                        mContext.getFeatureId());
+                handleSetTestProviderLocation();
                 return 0;
             }
             case "send-extra-command": {
-                String provider = getNextArgRequired();
-                String command = getNextArgRequired();
-                mService.sendExtraCommand(provider, command, null);
+                handleSendExtraCommand();
                 return 0;
             }
             default:
@@ -110,21 +95,47 @@
         }
     }
 
-    private int parseUserId() {
-        final String option = getNextOption();
-        if (option != null) {
-            if (option.equals("--user")) {
-                return UserHandle.parseUserArg(getNextArgRequired());
-            } else {
-                throw new IllegalArgumentException(
-                        "Expected \"--user\" option, but got \"" + option + "\" instead");
-            }
-        }
+    private void handleIsLocationEnabled() {
+        int userId = UserHandle.USER_CURRENT_OR_SELF;
 
-        return UserHandle.USER_CURRENT_OR_SELF;
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
+            if ("--user".equals(option)) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + option);
+            }
+        } while (true);
+
+        getOutPrintWriter().println(mService.isLocationEnabledForUser(userId));
     }
 
-    private ProviderProperties parseTestProviderProviderProperties() {
+    private void handleSetLocationEnabled() {
+        boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+
+        int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
+            if ("--user".equals(option)) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                throw new IllegalArgumentException("Unknown option: " + option);
+            }
+        } while (true);
+
+        mService.setLocationEnabledForUser(enabled, userId);
+    }
+
+    private void handleAddTestProvider() {
+        String provider = getNextArgRequired();
+
         boolean requiresNetwork = false;
         boolean requiresSatellite = false;
         boolean requiresCell = false;
@@ -135,8 +146,13 @@
         int powerRequirement = Criteria.POWER_LOW;
         int accuracy = Criteria.ACCURACY_FINE;
 
-        String option = getNextOption();
-        while (option != null) {
+        List<String> extraAttributionTags = Collections.emptyList();
+
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
             switch (option) {
                 case "--requiresNetwork": {
                     requiresNetwork = true;
@@ -174,12 +190,15 @@
                     accuracy = Integer.parseInt(getNextArgRequired());
                     break;
                 }
+                case "--extraAttributionTags": {
+                    extraAttributionTags = Arrays.asList(getNextArgRequired().split(","));
+                    break;
+                }
                 default:
                     throw new IllegalArgumentException(
                             "Received unexpected option: " + option);
             }
-            option = getNextOption();
-        }
+        } while(true);
 
         ProviderProperties properties = new ProviderProperties.Builder()
                 .setHasNetworkRequirement(requiresNetwork)
@@ -192,30 +211,50 @@
                 .setPowerUsage(powerRequirement)
                 .setAccuracy(accuracy)
                 .build();
-
-        return properties;
+        mService.addTestProvider(provider, properties, extraAttributionTags,
+                mContext.getOpPackageName(), mContext.getAttributionTag());
     }
 
-    private Location parseTestProviderLocation(String provider) {
-        boolean hasLatitude = false;
-        boolean hasLongitude = false;
+    private void handleRemoveTestProvider() {
+        String provider = getNextArgRequired();
+        mService.removeTestProvider(provider, mContext.getOpPackageName(),
+                mContext.getAttributionTag());
+    }
+
+    private void handleSetTestProviderEnabled() {
+        String provider = getNextArgRequired();
+        boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+        mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
+                mContext.getAttributionTag());
+    }
+
+    private void handleSetTestProviderLocation() {
+        String provider = getNextArgRequired();
+
+        boolean hasLatLng = false;
 
         Location location = new Location(provider);
         location.setAccuracy(DEFAULT_TEST_LOCATION_ACCURACY);
         location.setTime(System.currentTimeMillis());
+        location.setElapsedRealtimeNanos(System.nanoTime());
 
-        String option = getNextOption();
-        while (option != null) {
+        do {
+            String option = getNextOption();
+            if (option == null) {
+                break;
+            }
             switch (option) {
                 case "--location": {
                     String[] locationInput = getNextArgRequired().split(",");
                     if (locationInput.length != 2) {
-                        throw new IllegalArgumentException(
-                                "Unexpected location format: " + Arrays.toString(locationInput));
+                        throw new IllegalArgumentException("Location argument must be in the form "
+                                + "of \"<LATITUDE>,<LONGITUDE>\", not "
+                                + Arrays.toString(locationInput));
                     }
 
                     location.setLatitude(Double.parseDouble(locationInput[0]));
                     location.setLongitude(Double.parseDouble(locationInput[1]));
+                    hasLatLng = true;
                     break;
                 }
                 case "--accuracy": {
@@ -227,15 +266,22 @@
                     break;
                 }
                 default:
-                    throw new IllegalArgumentException(
-                            "Received unexpected option: " + option);
+                    throw new IllegalArgumentException("Unknown option: " + option);
             }
-            option = getNextOption();
+        } while (true);
+
+        if (!hasLatLng) {
+            throw new IllegalArgumentException("Option \"--location\" is required");
         }
 
-        location.setElapsedRealtimeNanos(System.nanoTime());
+        mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
+                mContext.getAttributionTag());
+    }
 
-        return location;
+    private void handleSendExtraCommand() {
+        String provider = getNextArgRequired();
+        String command = getNextArgRequired();
+        mService.sendExtraCommand(provider, command, null);
     }
 
     @Override
@@ -245,24 +291,29 @@
         pw.println("  help or -h");
         pw.println("    Print this help text.");
         pw.println("  is-location-enabled [--user <USER_ID>]");
-        pw.println("    Gets the master location switch enabled state.");
-        pw.println("  set-location-enabled [--user <USER_ID>] true|false");
-        pw.println("    Sets the master location switch enabled state.");
+        pw.println("    Gets the master location switch enabled state. If no user is specified,");
+        pw.println("    the current user is assumed.");
+        pw.println("  set-location-enabled true|false [--user <USER_ID>]");
+        pw.println("    Sets the master location switch enabled state. If no user is specified,");
+        pw.println("    the current user is assumed.");
         pw.println("  providers");
+        pw.println("    The providers command is followed by a subcommand, as listed below:");
+        pw.println();
         pw.println("    add-test-provider <PROVIDER> [--requiresNetwork] [--requiresSatellite]");
         pw.println("      [--requiresCell] [--hasMonetaryCost] [--supportsAltitude]");
         pw.println("      [--supportsSpeed] [--supportsBearing]");
         pw.println("      [--powerRequirement <POWER_REQUIREMENT>]");
+        pw.println("      [--extraAttributionTags <TAG>,<TAG>,...]");
         pw.println("      Add the given test provider. Requires MOCK_LOCATION permissions which");
         pw.println("      can be enabled by running \"adb shell appops set <uid>");
         pw.println("      android:mock_location allow\". There are optional flags that can be");
-        pw.println("      used to configure the provider properties. If no flags are included,");
-        pw.println("      then default values will be used.");
+        pw.println("      used to configure the provider properties and additional arguments. If");
+        pw.println("      no flags are included, then default values will be used.");
         pw.println("    remove-test-provider <PROVIDER>");
         pw.println("      Remove the given test provider.");
         pw.println("    set-test-provider-enabled <PROVIDER> true|false");
         pw.println("      Sets the given test provider enabled state.");
-        pw.println("    set-test-provider-location <PROVIDER> [--location <LATITUDE>,<LONGITUDE>]");
+        pw.println("    set-test-provider-location <PROVIDER> --location <LATITUDE>,<LONGITUDE>");
         pw.println("      [--accuracy <ACCURACY>] [--time <TIME>]");
         pw.println("      Set location for given test provider. Accuracy and time are optional.");
         pw.println("    send-extra-command <PROVIDER> <COMMAND>");
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
new file mode 100644
index 0000000..1967e02
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.gnss;
+
+import static com.android.server.location.gnss.GnssManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.location.GnssAntennaInfo;
+import android.location.IGnssAntennaInfoListener;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.listeners.BinderListenerRegistration;
+import com.android.server.location.listeners.ListenerMultiplexer;
+import com.android.server.location.listeners.ListenerRegistration;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Antenna info HAL module and listener multiplexer.
+ */
+public class GnssAntennaInfoProvider extends
+        ListenerMultiplexer<IBinder, IGnssAntennaInfoListener,
+                ListenerRegistration<IGnssAntennaInfoListener>, Void> implements
+        GnssNative.BaseCallbacks, GnssNative.AntennaInfoCallbacks {
+
+    /**
+     * Registration object for GNSS listeners.
+     */
+    protected class AntennaInfoListenerRegistration extends
+            BinderListenerRegistration<Void, IGnssAntennaInfoListener> {
+
+        protected AntennaInfoListenerRegistration(CallerIdentity callerIdentity,
+                IGnssAntennaInfoListener listener) {
+            super(null, callerIdentity, listener);
+        }
+
+        @Override
+        protected GnssAntennaInfoProvider getOwner() {
+            return GnssAntennaInfoProvider.this;
+        }
+    }
+
+    private final GnssNative mGnssNative;
+
+    private volatile @Nullable List<GnssAntennaInfo> mAntennaInfos;
+
+    GnssAntennaInfoProvider(GnssNative gnssNative) {
+        mGnssNative = gnssNative;
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addAntennaInfoCallbacks(this);
+    }
+
+    @Nullable List<GnssAntennaInfo> getAntennaInfos() {
+        return mAntennaInfos;
+    }
+
+    @Override
+    public String getTag() {
+        return TAG;
+    }
+
+    public boolean isSupported() {
+        return mGnssNative.isAntennaInfoSupported();
+    }
+
+    public void addListener(CallerIdentity callerIdentity, IGnssAntennaInfoListener listener) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            putRegistration(listener.asBinder(),
+                    new AntennaInfoListenerRegistration(callerIdentity, listener));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    public void removeListener(IGnssAntennaInfoListener listener) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            removeRegistration(listener.asBinder());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    protected boolean registerWithService(Void merged,
+            Collection<ListenerRegistration<IGnssAntennaInfoListener>> listenerRegistrations) {
+        return true;
+    }
+
+    @Override
+    protected void unregisterWithService() {}
+
+    @Override
+    protected boolean isActive(ListenerRegistration<IGnssAntennaInfoListener> registration) {
+        return true;
+    }
+
+    @Override
+    protected Void mergeRegistrations(
+            Collection<ListenerRegistration<IGnssAntennaInfoListener>> listenerRegistrations) {
+        return null;
+    }
+
+    @Override
+    public void onHalStarted() {
+        mGnssNative.startAntennaInfoListening();
+    }
+
+    @Override
+    public void onHalRestarted() {
+        mGnssNative.startAntennaInfoListening();
+    }
+
+    @Override
+    public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        if (antennaInfos.equals(mAntennaInfos)) {
+            return;
+        }
+
+        mAntennaInfos = antennaInfos;
+        deliverToListeners(listener -> {
+            listener.onGnssAntennaInfoChanged(antennaInfos);
+        });
+    }
+}
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 87e6ef4..5e6ae68 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -229,7 +229,7 @@
      * registrations will be treated as inactive and the backing service will never be registered.
      *
      */
-    protected boolean isServiceSupported() {
+    public boolean isSupported() {
         return true;
     }
 
@@ -276,7 +276,7 @@
 
     @Override
     protected boolean isActive(GnssListenerRegistration registration) {
-        if (!isServiceSupported()) {
+        if (!isSupported()) {
             return false;
         }
 
@@ -339,7 +339,7 @@
 
     @Override
     protected void onRegister() {
-        if (!isServiceSupported()) {
+        if (!isSupported()) {
             return;
         }
 
@@ -356,7 +356,7 @@
 
     @Override
     protected void onUnregister() {
-        if (!isServiceSupported()) {
+        if (!isSupported()) {
             return;
         }
 
@@ -404,7 +404,7 @@
 
     @Override
     protected String getServiceState() {
-        if (!isServiceSupported()) {
+        if (!isSupported()) {
             return "unsupported";
         } else {
             return super.getServiceState();
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 29da177..1df29ab 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -103,6 +103,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -372,7 +373,7 @@
     public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
             GnssMetrics gnssMetrics) {
         super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES,
-                /*locationTags*/ null);
+                Collections.emptySet());
 
         mContext = context;
         mGnssNative = gnssNative;
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 8312c63..bcdfed4 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -27,6 +27,7 @@
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssNmeaListener;
@@ -49,7 +50,6 @@
 import com.android.server.location.injector.Injector;
 
 import java.io.FileDescriptor;
-import java.util.ArrayList;
 import java.util.List;
 
 /** Manages Gnss providers and related Gnss functions for LocationManagerService. */
@@ -68,11 +68,11 @@
     private final GnssNmeaProvider mGnssNmeaProvider;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
+    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final IGpsGeofenceHardware mGnssGeofenceProxy;
 
     private final GnssGeofenceHalModule mGeofenceHalModule;
     private final GnssCapabilitiesHalModule mCapabilitiesHalModule;
-    private final GnssAntennaInfoHalModule mAntennaInfoHalModule;
 
     private final GnssMetrics mGnssMetrics;
 
@@ -89,11 +89,11 @@
         mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
         mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
         mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector, mGnssNative);
+        mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mGnssNative);
         mGnssGeofenceProxy = new GnssGeofenceProxy(mGnssNative);
 
         mGeofenceHalModule = new GnssGeofenceHalModule(mGnssNative);
         mCapabilitiesHalModule = new GnssCapabilitiesHalModule(mGnssNative);
-        mAntennaInfoHalModule = new GnssAntennaInfoHalModule(mGnssNative);
 
         // allow gnss access to begin - we must assume that callbacks can start immediately
         mGnssNative.register();
@@ -140,7 +140,7 @@
      * Get GNSS antenna information.
      */
     public @Nullable List<GnssAntennaInfo> getGnssAntennaInfos() {
-        return mAntennaInfoHalModule.getAntennaInfos();
+        return mGnssAntennaInfoProvider.getAntennaInfos();
     }
 
     /**
@@ -242,6 +242,24 @@
     }
 
     /**
+     * Adds a GNSS antenna info listener.
+     */
+    public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
+            @Nullable String attributionTag, String listenerId) {
+
+        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
+                listenerId);
+        mGnssAntennaInfoProvider.addListener(identity, listener);
+    }
+
+    /**
+     * Removes a GNSS antenna info listener.
+     */
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        mGnssAntennaInfoProvider.removeListener(listener);
+    }
+
+    /**
      * Send Ni Response, indicating a location request initiated by a network carrier.
      */
     public void sendNiResponse(int notifId, int userResponse) {
@@ -263,25 +281,34 @@
 
         ipw.println("Capabilities: " + mGnssNative.getCapabilities());
 
-        List<GnssAntennaInfo> infos = mAntennaInfoHalModule.getAntennaInfos();
-        if (infos != null) {
-            ipw.println("Antenna Infos: " + infos);
+        if (mGnssStatusProvider.isSupported()) {
+            ipw.println("Status Provider:");
+            ipw.increaseIndent();
+            mGnssStatusProvider.dump(fd, ipw, args);
+            ipw.decreaseIndent();
         }
 
-        ipw.println("Measurements Provider:");
-        ipw.increaseIndent();
-        mGnssMeasurementsProvider.dump(fd, ipw, args);
-        ipw.decreaseIndent();
+        if (mGnssMeasurementsProvider.isSupported()) {
+            ipw.println("Measurements Provider:");
+            ipw.increaseIndent();
+            mGnssMeasurementsProvider.dump(fd, ipw, args);
+            ipw.decreaseIndent();
+        }
 
-        ipw.println("Navigation Message Provider:");
-        ipw.increaseIndent();
-        mGnssNavigationMessageProvider.dump(fd, ipw, args);
-        ipw.decreaseIndent();
+        if (mGnssNavigationMessageProvider.isSupported()) {
+            ipw.println("Navigation Message Provider:");
+            ipw.increaseIndent();
+            mGnssNavigationMessageProvider.dump(fd, ipw, args);
+            ipw.decreaseIndent();
+        }
 
-        ipw.println("Status Provider:");
-        ipw.increaseIndent();
-        mGnssStatusProvider.dump(fd, ipw, args);
-        ipw.decreaseIndent();
+        if (mGnssAntennaInfoProvider.isSupported()) {
+            ipw.println("Navigation Message Provider:");
+            ipw.increaseIndent();
+            ipw.println("Antenna Infos: " + mGnssAntennaInfoProvider.getAntennaInfos());
+            mGnssAntennaInfoProvider.dump(fd, ipw, args);
+            ipw.decreaseIndent();
+        }
 
         GnssPowerStats powerStats = mGnssNative.getPowerStats();
         if (powerStats != null) {
@@ -399,53 +426,4 @@
             }
         }
     }
-
-    private class GnssAntennaInfoHalModule implements GnssNative.BaseCallbacks,
-            GnssNative.AntennaInfoCallbacks {
-
-        private final GnssNative mGnssNative;
-
-        private volatile @Nullable List<GnssAntennaInfo> mAntennaInfos;
-
-        GnssAntennaInfoHalModule(GnssNative gnssNative) {
-            mGnssNative = gnssNative;
-            mGnssNative.addBaseCallbacks(this);
-            mGnssNative.addAntennaInfoCallbacks(this);
-        }
-
-        @Nullable List<GnssAntennaInfo> getAntennaInfos() {
-            return mAntennaInfos;
-        }
-
-        @Override
-        public void onHalStarted() {
-            mGnssNative.startAntennaInfoListening();
-        }
-
-        @Override
-        public void onHalRestarted() {
-            mGnssNative.startAntennaInfoListening();
-        }
-
-        @Override
-        public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-            if (antennaInfos.equals(mAntennaInfos)) {
-                return;
-            }
-
-            mAntennaInfos = antennaInfos;
-
-            long ident = Binder.clearCallingIdentity();
-            try {
-                Intent intent = new Intent(LocationManager.ACTION_GNSS_ANTENNA_INFOS_CHANGED)
-                        .putParcelableArrayListExtra(LocationManager.EXTRA_GNSS_ANTENNA_INFOS,
-                                new ArrayList<>(antennaInfos))
-                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
-                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 305bc9b..b3119d7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -99,7 +99,7 @@
     }
 
     @Override
-    protected boolean isServiceSupported() {
+    public boolean isSupported() {
         return mGnssNative.isMeasurementSupported();
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index ff9ad65..e9fce05 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -70,7 +70,7 @@
     }
 
     @Override
-    protected boolean isServiceSupported() {
+    public boolean isSupported() {
         return mGnssNative.isNavigationMessageCollectionSupported();
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 7e2f089..f275663 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -699,11 +699,11 @@
     }
 
     /**
-     * Returns true if antenna info listening is supported.
+     * Returns true if antenna info is supported.
      */
-    public boolean isAntennaInfoListeningSupported() {
+    public boolean isAntennaInfoSupported() {
         Preconditions.checkState(mRegistered);
-        return mGnssHal.isAntennaInfoListeningSupported();
+        return mGnssHal.isAntennaInfoSupported();
     }
 
     /**
@@ -1259,7 +1259,7 @@
             return native_stop_navigation_message_collection();
         }
 
-        protected boolean isAntennaInfoListeningSupported() {
+        protected boolean isAntennaInfoSupported() {
             return native_is_antenna_info_supported();
         }
 
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 9ff6e6b..ba7f44f 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -28,6 +28,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -66,9 +67,10 @@
 
         /**
          * Default state value for a location provider that is disabled with no properties and an
-         * empty provider package list.
+         * empty extra attribution tag set.
          */
-        public static final State EMPTY_STATE = new State(false, null, null, null);
+        public static final State EMPTY_STATE = new State(false, null, null,
+                Collections.emptySet());
 
         /**
          * The provider's allowed state.
@@ -85,14 +87,18 @@
          */
         @Nullable public final CallerIdentity identity;
 
-        @Nullable public final Set<String> locationTags;
+        /**
+         * A set of attribution tags also associated with this provider - these attribution tags may
+         * be afforded special privileges.
+         */
+        public final Set<String> extraAttributionTags;
 
         private State(boolean allowed, ProviderProperties properties, CallerIdentity identity,
-                Set<String> locationTags) {
+                Set<String> extraAttributionTags) {
             this.allowed = allowed;
             this.properties = properties;
             this.identity = identity;
-            this.locationTags = locationTags;
+            this.extraAttributionTags = Objects.requireNonNull(extraAttributionTags);
         }
 
         /**
@@ -102,7 +108,7 @@
             if (allowed == this.allowed) {
                 return this;
             } else {
-                return new State(allowed, properties, identity, locationTags);
+                return new State(allowed, properties, identity, extraAttributionTags);
             }
         }
 
@@ -113,7 +119,7 @@
             if (Objects.equals(properties, this.properties)) {
                 return this;
             } else {
-                return new State(allowed, properties, identity, locationTags);
+                return new State(allowed, properties, identity, extraAttributionTags);
             }
         }
 
@@ -124,18 +130,18 @@
             if (Objects.equals(identity, this.identity)) {
                 return this;
             } else {
-                return new State(allowed, properties, identity, locationTags);
+                return new State(allowed, properties, identity, extraAttributionTags);
             }
         }
 
         /**
-         * Returns a state the same as the current but with location tags set as specified.
+         * Returns a state the same as the current but with extra attribution tags set as specified.
          */
-        public State withLocationTags(@Nullable Set<String> locationTags) {
-            if (Objects.equals(locationTags, this.locationTags)) {
+        public State withExtraAttributionTags(Set<String> extraAttributionTags) {
+            if (extraAttributionTags.equals(this.extraAttributionTags)) {
                 return this;
             } else {
-                return new State(allowed, properties, identity, locationTags);
+                return new State(allowed, properties, identity, extraAttributionTags);
             }
         }
 
@@ -151,12 +157,12 @@
             State state = (State) o;
             return allowed == state.allowed && properties == state.properties
                     && Objects.equals(identity, state.identity)
-                    && Objects.equals(locationTags, state.locationTags);
+                    && extraAttributionTags.equals(state.extraAttributionTags);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(allowed, properties, identity, locationTags);
+            return Objects.hash(allowed, properties, identity, extraAttributionTags);
         }
     }
 
@@ -213,14 +219,14 @@
      * An optional identity and properties may be provided to initialize the location provider.
      */
     protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
-            @Nullable ProviderProperties properties, @Nullable Set<String> locationTags) {
+            @Nullable ProviderProperties properties, Set<String> extraAttributionTags) {
         Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
-        mExecutor = executor;
+        mExecutor = Objects.requireNonNull(executor);
         mInternalState = new AtomicReference<>(new InternalState(null,
                 State.EMPTY_STATE
                         .withIdentity(identity)
-                        .withProperties(properties).withLocationTags(locationTags))
-        );
+                        .withProperties(properties)
+                        .withExtraAttributionTags(extraAttributionTags)));
         mController = new Controller();
     }
 
@@ -292,7 +298,7 @@
     }
 
     /**
-     * Call this method to report a change in provider packages.
+     * Call this method to report a change in the provider's identity.
      */
     protected void setIdentity(@Nullable CallerIdentity identity) {
         Preconditions.checkArgument(identity == null || identity.getListenerId() == null);
@@ -300,10 +306,10 @@
     }
 
     /**
-     * Call this method to report a change in provider location tags.
+     * Call this method to report a change in the provider's extra attribution tags.
      */
-    protected void setLocationTags(@Nullable Set<String> locationTags) {
-        setState(state -> state.withLocationTags(locationTags));
+    protected void setExtraAttributionTags(Set<String> extraAttributionTags) {
+        setState(state -> state.withExtraAttributionTags(extraAttributionTags));
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
index 49f6e64..423f833 100644
--- a/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/DelegateLocationProvider.java
@@ -24,6 +24,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 import java.util.concurrent.Executor;
 
 /**
@@ -40,7 +41,7 @@
     private boolean mInitialized = false;
 
     DelegateLocationProvider(Executor executor, AbstractLocationProvider delegate) {
-        super(executor, null, null, null);
+        super(executor, null, null, Collections.emptySet());
 
         mDelegate = delegate;
     }
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 1ecf3ee..fef30f9 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -2265,8 +2265,8 @@
             onEnabledChanged(UserHandle.USER_ALL);
         }
 
-        if (!Objects.equals(oldState.locationTags, newState.locationTags)) {
-            if (mOnLocationTagsChangeListener != null) {
+        if (mOnLocationTagsChangeListener != null) {
+            if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)) {
                 if (oldState.identity != null) {
                     FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
                             OnProviderLocationTagsChangeListener::onLocationTagsChanged,
@@ -2280,7 +2280,7 @@
                             OnProviderLocationTagsChangeListener::onLocationTagsChanged,
                             mOnLocationTagsChangeListener, new LocationTagInfo(
                                     newState.identity.getUid(), newState.identity.getPackageName(),
-                                    newState.locationTags)
+                                    newState.extraAttributionTags)
                             ));
                 }
             }
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 7660f56..52b04d4 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -40,9 +40,9 @@
     @Nullable private Location mLocation;
 
     public MockLocationProvider(ProviderProperties properties, CallerIdentity identity,
-            @Nullable Set<String> locationTags) {
+            Set<String> extraAttributionTags) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR, identity, properties, locationTags);
+        super(DIRECT_EXECUTOR, identity, properties, extraAttributionTags);
     }
 
     /** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index 4ffa9a5..8193644 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -31,6 +31,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 
 /**
  * Represents a location provider that may switch between a mock implementation and a real
@@ -75,7 +76,7 @@
     public MockableLocationProvider(Object ownerLock) {
         // using a direct executor is acceptable because all inbound calls are delegated to the
         // actual provider implementations which will use their own executors
-        super(DIRECT_EXECUTOR, null, null, null);
+        super(DIRECT_EXECUTOR, null, null, Collections.emptySet());
         mOwnerLock = ownerLock;
         mRequest = ProviderRequest.EMPTY_REQUEST;
     }
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
index ee9d35d..68ede88 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
@@ -30,6 +30,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 
 /**
  * A passive location provider reports locations received from other providers
@@ -48,7 +49,7 @@
     public PassiveLocationProvider(Context context) {
         // using a direct executor is ok because this class has no locks that could deadlock
         super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES,
-                /*locationTags*/ null);
+                Collections.emptySet());
         setAllowed(true);
     }
 
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index f00478a..44b62b3 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -44,6 +43,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -52,8 +52,8 @@
  */
 public class ProxyLocationProvider extends AbstractLocationProvider {
 
-    private static final String KEY_LOCATION_TAGS = "android:location_allow_listed_tags";
-    private static final String LOCATION_TAGS_SEPARATOR = ";";
+    private static final String KEY_EXTRA_ATTRIBUTION_TAGS = "android:location_allow_listed_tags";
+    private static final String EXTRA_ATTRIBUTION_TAGS_SEPARATOR = ";";
 
     /**
      * Creates and registers this proxy. If no suitable service is available for the proxy, returns
@@ -90,7 +90,7 @@
             int nonOverlayPackageResId) {
         // safe to use direct executor since our locks are not acquired in a code path invoked by
         // our owning provider
-        super(DIRECT_EXECUTOR, null, null, null);
+        super(DIRECT_EXECUTOR, null, null, Collections.emptySet());
 
         mContext = context;
         mServiceWatcher = new ServiceWatcher(context, action, this::onBind,
@@ -100,16 +100,6 @@
         mRequest = ProviderRequest.EMPTY_REQUEST;
     }
 
-    private void updateLocationTagInfo(@NonNull BoundService boundService) {
-        if (boundService.metadata != null) {
-            final String tagsList = boundService.metadata.getString(KEY_LOCATION_TAGS);
-            if (tagsList != null) {
-                final String[] tags = tagsList.split(LOCATION_TAGS_SEPARATOR);
-                setLocationTags(new ArraySet<>(tags));
-            }
-        }
-    }
-
     private boolean checkServiceResolves() {
         return mServiceWatcher.checkServiceResolves();
     }
@@ -120,14 +110,22 @@
         synchronized (mLock) {
             mProxy = new Proxy();
             mService = boundService.component;
+
+            // update extra attribution tag info from manifest
+            if (boundService.metadata != null) {
+                String tagsList = boundService.metadata.getString(KEY_EXTRA_ATTRIBUTION_TAGS);
+                if (tagsList != null) {
+                    setExtraAttributionTags(
+                            new ArraySet<>(tagsList.split(EXTRA_ATTRIBUTION_TAGS_SEPARATOR)));
+                }
+            }
+
             provider.setLocationProviderManager(mProxy);
 
             ProviderRequest request = mRequest;
             if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
                 provider.setRequest(request);
             }
-
-            updateLocationTagInfo(boundService);
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS
index 08b8a8c..7577ee5 100644
--- a/services/core/java/com/android/server/locksettings/OWNERS
+++ b/services/core/java/com/android/server/locksettings/OWNERS
@@ -1,3 +1,4 @@
 jaggies@google.com
 kchyn@google.com
 rubinxu@google.com
+xunchang@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f8cb2e4..274344a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -64,6 +64,8 @@
 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.UserHandle.USER_NULL;
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
@@ -6001,10 +6003,11 @@
                 for (int i = 0; i < intentCount; i++) {
                     PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                     if (pendingIntent != null) {
-                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
+                        am.setPendingIntentAllowlistDuration(pendingIntent.getTarget(),
                                 ALLOWLIST_TOKEN, duration,
-                                BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED
-                        );
+                                TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+                                REASON_NOTIFICATION_SERVICE,
+                                "NotificationManagerService");
                         am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
                                 ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
                                         | FLAG_SERVICE_SENDER));
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2812830..a5e28f1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -530,14 +530,6 @@
             throw new SecurityException("User restriction prevents installing");
         }
 
-        if (params.dataLoaderParams != null
-                && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
-                        != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("You need the "
-                    + "com.android.permission.USE_INSTALLER_V2 permission "
-                    + "to use a data loader");
-        }
-
         // INSTALL_REASON_ROLLBACK allows an app to be rolled back without requiring the ROLLBACK
         // capability; ensure if this is set as the install reason the app has one of the necessary
         // signature permissions to perform the rollback.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7bf3c5c..460b2f2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -3547,14 +3547,12 @@
 
     @Override
     public DataLoaderParamsParcel getDataLoaderParams() {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
     }
 
     @Override
     public void addFile(int location, String name, long lengthBytes, byte[] metadata,
             byte[] signature) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
@@ -3587,7 +3585,6 @@
 
     @Override
     public void removeFile(int location, String name) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8123951..a4c0655 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2604,10 +2604,22 @@
             final ArrayList<ResolveInfo> matchAllList = new ArrayList<>();
             final ArrayList<ResolveInfo> undefinedList = new ArrayList<>();
 
+            // Blocking instant apps is usually done in applyPostResolutionFilter, but since
+            // domain verification can resolve to a single result, which can be an instant app,
+            // it will then be filtered to an empty list in that method. Instead, do blocking
+            // here so that instant apps can be ignored for approval filtering and a lower
+            // priority result chosen instead.
+            final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
+
             final int count = candidates.size();
             // First, try to use approved apps.
             for (int n = 0; n < count; n++) {
                 ResolveInfo info = candidates.get(n);
+                if (blockInstant && (info.isInstantAppAvailable
+                        || isInstantApp(info.activityInfo.packageName, userId))) {
+                    continue;
+                }
+
                 // Add to the special match all list (Browser use case)
                 if (info.handleAllWebDataURI) {
                     matchAllList.add(info);
@@ -5092,23 +5104,12 @@
                         InstallArgs args = data.args;
                         PackageInstalledInfo parentRes = data.res;
 
-                        final boolean grantPermissions = (args.installFlags
-                                & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
                         final boolean killApp = (args.installFlags
                                 & PackageManager.INSTALL_DONT_KILL_APP) == 0;
                         final boolean virtualPreload = ((args.installFlags
                                 & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
-                        final String[] grantedPermissions = args.installGrantPermissions;
-                        final List<String> whitelistedRestrictedPermissions = ((args.installFlags
-                                & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0
-                                    && parentRes.pkg != null)
-                                ? parentRes.pkg.getRequestedPermissions()
-                                : args.whitelistedRestrictedPermissions;
-                        int autoRevokePermissionsMode = args.autoRevokePermissionsMode;
 
-                        handlePackagePostInstall(parentRes, grantPermissions,
-                                killApp, virtualPreload, grantedPermissions,
-                                whitelistedRestrictedPermissions, autoRevokePermissionsMode,
+                        handlePackagePostInstall(parentRes, killApp, virtualPreload,
                                 didRestore, args.installSource.installerPackageName, args.observer,
                                 args.mDataLoaderType);
 
@@ -5387,11 +5388,8 @@
         }
     }
 
-    private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
-            boolean killApp, boolean virtualPreload,
-            String[] grantedPermissions, List<String> allowlistedRestrictedPermissions,
-            int autoRevokePermissionsMode,
-            boolean launchedForRestore, String installerPackage,
+    private void handlePackagePostInstall(PackageInstalledInfo res, boolean killApp,
+            boolean virtualPreload, boolean launchedForRestore, String installerPackage,
             IPackageInstallObserver2 installObserver, int dataLoaderType) {
         boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
         final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
@@ -5422,29 +5420,6 @@
                 res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
             }
 
-            final PermissionManagerServiceInternal.PackageInstalledParams.Builder
-                    permissionParamsBuilder =
-                    new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
-            final List<String> grantedPermissionsList;
-            if (grantPermissions) {
-                if (grantedPermissions != null) {
-                    permissionParamsBuilder.setGrantedPermissions(Arrays.asList(
-                            grantedPermissions));
-                } else {
-                    permissionParamsBuilder.setGrantedPermissions(
-                            res.pkg.getRequestedPermissions());
-                }
-            }
-            if (allowlistedRestrictedPermissions != null) {
-                permissionParamsBuilder.setAllowlistedRestrictedPermissions(
-                        allowlistedRestrictedPermissions);
-            }
-            permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
-            for (final int userId : res.newUsers) {
-                mPermissionManager.onPackageInstalled(res.pkg, permissionParamsBuilder.build(),
-                        userId);
-            }
-
             final String installerPackageName =
                     res.installerPackageName != null
                             ? res.installerPackageName
@@ -18445,6 +18420,37 @@
                 }
 
                 mSettings.writeKernelMappingLPr(ps);
+
+                final PermissionManagerServiceInternal.PackageInstalledParams.Builder
+                        permissionParamsBuilder =
+                        new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
+                final boolean grantPermissions = (installArgs.installFlags
+                        & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
+                if (grantPermissions) {
+                    final List<String> grantedPermissions =
+                            installArgs.installGrantPermissions != null
+                                    ? Arrays.asList(installArgs.installGrantPermissions)
+                                    : pkg.getRequestedPermissions();
+                    permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
+                }
+                final boolean allowlistAllRestrictedPermissions =
+                        (installArgs.installFlags
+                                & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
+                final List<String> allowlistedRestrictedPermissions =
+                        allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
+                        : installArgs.whitelistedRestrictedPermissions;
+                if (allowlistedRestrictedPermissions != null) {
+                    permissionParamsBuilder.setAllowlistedRestrictedPermissions(
+                            allowlistedRestrictedPermissions);
+                }
+                final int autoRevokePermissionsMode = installArgs.autoRevokePermissionsMode;
+                permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
+                for (int currentUserId : allUsersList) {
+                    if (ps.getInstalled(currentUserId)) {
+                        mPermissionManager.onPackageInstalled(pkg, permissionParamsBuilder.build(),
+                                currentUserId);
+                    }
+                }
             }
             res.name = pkgName;
             res.uid = pkg.getUid();
@@ -22673,7 +22679,7 @@
         }
         final Intent intent = getHomeIntent();
         final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
-                PackageManager.GET_META_DATA, userId);
+                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
         final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
                 intent, null, 0, resolveInfos, 0, true, false, false, userId);
         final String packageName = preferredResolveInfo != null
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index a83a3f8..38e100e 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -787,6 +787,10 @@
         return firstInstallTime;
     }
 
+    public String getName() {
+        return name;
+    }
+
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
         setPath(other.getPath());
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index bb4ec16..a604afc 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -19,15 +19,22 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.Person;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchSession;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.SetSchemaRequest;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.LocusId;
+import android.content.pm.AppSearchPerson;
+import android.content.pm.AppSearchShortcutInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
+import android.os.Binder;
 import android.os.PersistableBundle;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
@@ -39,9 +46,11 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.ShortcutService.DumpFilter;
@@ -64,8 +73,12 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 /**
@@ -155,6 +168,16 @@
 
     private long mLastKnownForegroundElapsedTime;
 
+    private final Object mLock = new Object();
+
+    /**
+     * All external packages that have gained access to the shortcuts from this package
+     */
+    private final Map<String, PackageIdentifier> mPackageIdentifiers = new ArrayMap<>(0);
+
+    @GuardedBy("mLock")
+    private AppSearchSession mAppSearchSession;
+
     private ShortcutPackage(ShortcutUser shortcutUser,
             int packageUserId, String packageName, ShortcutPackageInfo spi) {
         super(shortcutUser, packageUserId, packageName,
@@ -2140,6 +2163,15 @@
         }
     }
 
+    void updateVisibility(String packageName, byte[] certificate, boolean visible) {
+        if (visible) {
+            mPackageIdentifiers.put(packageName, new PackageIdentifier(packageName, certificate));
+        } else {
+            mPackageIdentifiers.remove(packageName);
+        }
+        resetAppSearch(null);
+    }
+
     private boolean verifyRanksSequential(List<ShortcutInfo> list) {
         boolean failed = false;
 
@@ -2153,4 +2185,128 @@
         }
         return failed;
     }
+
+    private void runInAppSearch(
+            Function<SearchSessionObservable, Consumer<AppSearchSession>>... observers) {
+        if (mShortcutUser == null) {
+            Slog.w(TAG, "shortcut user is null");
+            return;
+        }
+        synchronized (mLock) {
+            if (mAppSearchSession != null) {
+                final CountDownLatch latch = new CountDownLatch(1);
+                final long callingIdentity = Binder.clearCallingIdentity();
+                try {
+                    final SearchSessionObservable upstream =
+                            new SearchSessionObservable(mAppSearchSession, latch);
+                    for (Function<SearchSessionObservable, Consumer<AppSearchSession>> observer
+                            : observers) {
+                        upstream.map(observer);
+                    }
+                    upstream.next();
+                } finally {
+                    Binder.restoreCallingIdentity(callingIdentity);
+                }
+                ConcurrentUtils.waitForCountDownNoInterrupt(latch, 500,
+                        "timeout accessing shortcut");
+            } else {
+                resetAppSearch(observers);
+            }
+        }
+    }
+
+    private void resetAppSearch(
+            Function<SearchSessionObservable, Consumer<AppSearchSession>>... observers) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final AppSearchManager.SearchContext searchContext =
+                new AppSearchManager.SearchContext.Builder()
+                        .setDatabaseName(getPackageName()).build();
+        mShortcutUser.runInAppSearch(searchContext, result -> {
+            if (!result.isSuccess()) {
+                Slog.e(TAG, "error getting search session during lazy init, "
+                        + result.getErrorMessage());
+                latch.countDown();
+                return;
+            }
+            // TODO: Flatten callback chain with proper async framework
+            final SearchSessionObservable upstream =
+                    new SearchSessionObservable(result.getResultValue(), latch)
+                            .map(this::setupSchema);
+            if (observers != null) {
+                for (Function<SearchSessionObservable, Consumer<AppSearchSession>> observer
+                        : observers) {
+                    upstream.map(observer);
+                }
+            }
+            upstream.map(observable -> session -> {
+                mAppSearchSession = session;
+                observable.next();
+            });
+            upstream.next();
+        });
+        ConcurrentUtils.waitForCountDownNoInterrupt(latch, 1500,
+                "timeout accessing shortcut during lazy initialization");
+    }
+
+    /**
+     * creates the schema for shortcut in the database
+     */
+    private Consumer<AppSearchSession> setupSchema(SearchSessionObservable observable) {
+        return session -> {
+            SetSchemaRequest.Builder schemaBuilder = new SetSchemaRequest.Builder()
+                            .addSchemas(AppSearchPerson.SCHEMA, AppSearchShortcutInfo.SCHEMA);
+            for (PackageIdentifier pi : mPackageIdentifiers.values()) {
+                schemaBuilder = schemaBuilder
+                        .setSchemaTypeVisibilityForPackage(
+                                AppSearchPerson.SCHEMA_TYPE, true, pi)
+                        .setSchemaTypeVisibilityForPackage(
+                                AppSearchShortcutInfo.SCHEMA_TYPE, true, pi);
+            }
+            session.setSchema(schemaBuilder.build(), mShortcutUser.mExecutor, result -> {
+                if (!result.isSuccess()) {
+                    observable.error("failed to instantiate app search schema: "
+                            + result.getErrorMessage());
+                    return;
+                }
+                observable.next();
+            });
+        };
+    }
+
+    /**
+     * TODO: Replace this temporary implementation with proper async framework
+     */
+    private class SearchSessionObservable {
+
+        final AppSearchSession mSession;
+        final CountDownLatch mLatch;
+        final ArrayList<Consumer<AppSearchSession>> mObservers = new ArrayList<>(1);
+
+        SearchSessionObservable(@NonNull final AppSearchSession session,
+                @NonNull final CountDownLatch latch) {
+            mSession = session;
+            mLatch = latch;
+        }
+
+        SearchSessionObservable map(
+                Function<SearchSessionObservable, Consumer<AppSearchSession>> observer) {
+            mObservers.add(observer.apply(this));
+            return this;
+        }
+
+        void next() {
+            if (mObservers.isEmpty()) {
+                mLatch.countDown();
+                return;
+            }
+            mObservers.remove(0).accept(mSession);
+        }
+
+        void error(@Nullable final String errorMessage) {
+            if (errorMessage != null) {
+                Slog.e(TAG, errorMessage);
+            }
+            mLatch.countDown();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 4d8abea..209a143 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2150,6 +2150,15 @@
     }
 
     @Override
+    public void updateShortcutVisibility(String callingPkg, String packageName, byte[] certificate,
+            boolean visible, int userId) {
+        synchronized (mLock) {
+            getPackageShortcutsForPublisherLocked(callingPkg, userId)
+                    .updateVisibility(packageName, certificate, visible);
+        }
+    }
+
+    @Override
     public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
             IntentSender resultIntent, int userId) {
         Objects.requireNonNull(shortcut);
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 3e3aa67..6cbc47f 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -18,9 +18,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSession;
 import android.content.pm.ShortcutManager;
 import android.metrics.LogMaker;
+import android.os.Binder;
 import android.os.FileUtils;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
@@ -32,6 +37,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.FgThread;
 import com.android.server.pm.ShortcutService.DumpFilter;
 import com.android.server.pm.ShortcutService.InvalidFileFormatException;
 
@@ -45,6 +51,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -111,6 +118,8 @@
     }
 
     final ShortcutService mService;
+    final AppSearchManager mAppSearchManager;
+    final Executor mExecutor;
 
     @UserIdInt
     private final int mUserId;
@@ -132,6 +141,9 @@
     public ShortcutUser(ShortcutService service, int userId) {
         mService = service;
         mUserId = userId;
+        mAppSearchManager = service.mContext.createContextAsUser(UserHandle.of(userId), 0)
+                .getSystemService(AppSearchManager.class);
+        mExecutor = FgThread.getExecutor();
     }
 
     public int getUserId() {
@@ -693,4 +705,18 @@
         logger.write(logMaker.setType(MetricsEvent.SHORTCUTS_CHANGED_SHORTCUT_COUNT)
                 .setSubtype(totalSharingShortcutCount));
     }
+
+    void runInAppSearch(@NonNull final AppSearchManager.SearchContext searchContext,
+            @NonNull final Consumer<AppSearchResult<AppSearchSession>> callback) {
+        if (mAppSearchManager == null) {
+            Slog.e(TAG, "app search manager is null");
+            return;
+        }
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            mAppSearchManager.createSearchSession(searchContext, mExecutor, callback);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 24c27be..c75dd27 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -983,7 +983,11 @@
 
     @Override
     public UserInfo getProfileParent(@UserIdInt int userId) {
-        checkManageUsersPermission("get the profile parent");
+        if (!hasManageUsersOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+            throw new SecurityException(
+                    "You need MANAGE_USERS or INTERACT_ACROSS_USERS permission to get the "
+                            + "profile parent");
+        }
         synchronized (mUsersLock) {
             return getProfileParentLU(userId);
         }
@@ -1531,11 +1535,14 @@
 
     @Override
     public String getUserName() {
-        if (!hasManageUsersOrPermission(android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED)) {
-            throw new SecurityException("You need MANAGE_USERS or GET_ACCOUNTS_PRIVILEGED "
-                    + "permissions to: get user name");
+        final int callingUid = Binder.getCallingUid();
+        if (!hasManageOrCreateUsersPermission()
+                || hasPermissionGranted(
+                        android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, callingUid)) {
+            throw new SecurityException("You need MANAGE_USERS or CREATE_USERS or "
+                    + "GET_ACCOUNTS_PRIVILEGED permissions to: get user name");
         }
-        final int userId = UserHandle.getUserId(Binder.getCallingUid());
+        final int userId = UserHandle.getUserId(callingUid);
         synchronized (mUsersLock) {
             UserInfo userInfo = userWithName(getUserInfoLU(userId));
             return userInfo == null ? "" : userInfo.name;
@@ -3287,7 +3294,7 @@
      * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
      */
     @Override
-    public UserInfo createProfileForUserWithThrow(String name, @NonNull String userType,
+    public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
         checkManageOrCreateUsersPermission(flags);
@@ -3868,7 +3875,7 @@
      * @hide
      */
     @Override
-    public UserInfo createRestrictedProfileWithThrow(String name, int parentUserId) {
+    public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) {
         checkManageOrCreateUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUserWithThrow(
                 name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 44a2187..e3ccb75 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1277,12 +1277,7 @@
                     newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);
 
                     // If we are allowlisting the permission, update the exempt flag before grant.
-                    // If the permission can't be allowlisted by an installer, skip it here because
-                    // this is where the platform takes the role of the installer for exempting
-                    // preinstalled apps.
-                    if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)
-                            && !pm.getPermissionInfo(permission).isInstallerExemptIgnored()) {
-
+                    if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)) {
                         pm.updatePermissionFlags(permission, pkg,
                                 PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
                                 PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 32bee58..ac50f29 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -239,10 +239,6 @@
         return (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0;
     }
 
-    public boolean isInstallerExemptIgnored() {
-        return (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0;
-    }
-
     public boolean isSignature() {
         return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                 == PermissionInfo.PROTECTION_SIGNATURE;
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 7a936ec..2dfb6ff 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -24,14 +24,12 @@
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
 import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
@@ -771,10 +769,6 @@
 
             isRuntimePermission = bp.isRuntime();
 
-            if (bp.isInstallerExemptIgnored()) {
-                flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-            }
-
             final UidPermissionState uidState = getUidStateLocked(pkg, userId);
             if (uidState == null) {
                 Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
@@ -1018,8 +1012,7 @@
         Preconditions.checkFlagsArgument(flags,
                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
                         | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
-                        | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
-                        | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE);
+                        | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
         Preconditions.checkArgumentNonNegative(userId, null);
 
         if (UserHandle.getCallingUserId() != userId) {
@@ -1043,9 +1036,9 @@
         final boolean isCallerInstallerOnRecord =
                 mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid);
 
-        if ((flags & (PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
-                | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE)) != 0 && !isCallerPrivileged) {
-            throw new SecurityException("Querying system or role allowlist requires "
+        if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0
+                && !isCallerPrivileged) {
+            throw new SecurityException("Querying system allowlist requires "
                     + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
         }
 
@@ -1087,9 +1080,6 @@
             if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
                 queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
             }
-            if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
-                queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-            }
 
             ArrayList<String> allowlistedPermissions = null;
 
@@ -1182,8 +1172,7 @@
         Preconditions.checkFlagsArgument(flags,
                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
                         | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
-                        | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
-                        | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE);
+                        | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER);
         Preconditions.checkArgument(Integer.bitCount(flags) == 1);
         Preconditions.checkArgumentNonNegative(userId, null);
 
@@ -1209,10 +1198,8 @@
         final boolean isCallerInstallerOnRecord =
                 mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid);
 
-        if ((flags & (PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
-                | PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE)) != 0
-                && !isCallerPrivileged) {
-            throw new SecurityException("Modifying system or role allowlist requires "
+        if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) {
+            throw new SecurityException("Modifying system allowlist requires "
                     + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS);
         }
 
@@ -3718,15 +3705,6 @@
                         }
                     }
                     break;
-                    case FLAG_PERMISSION_ALLOWLIST_ROLE: {
-                        mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                        if (permissions != null && permissions.contains(permissionName)) {
-                            newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                        } else {
-                            newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                        }
-                    }
-                    break;
                 }
             }
 
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
index 080de73..e3cf67c 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationCollector.java
@@ -42,6 +42,8 @@
     private static final Pattern DOMAIN_NAME_WITH_WILDCARD =
             Pattern.compile("(\\*\\.)?" + Patterns.DOMAIN_NAME.pattern());
 
+    private static final int MAX_DOMAINS_BYTE_SIZE = 1024 * 1024;
+
     @NonNull
     private final PlatformCompat mPlatformCompat;
 
@@ -71,7 +73,7 @@
      *     <li>- Only IntentFilter.SCHEME_HTTP and/or IntentFilter.SCHEME_HTTPS,
      *           with no other schemes</li>
      * </ul>
-     *
+     * <p>
      * On prior versions of Android, Intent.CATEGORY_BROWSABLE was not a requirement, other
      * schemes were allowed, and setting autoVerify to true in any intent filter would implicitly
      * pretend that all intent filters were set to autoVerify="true".
@@ -86,8 +88,8 @@
     }
 
     /**
-     * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires
-     * {@link IntentFilter#getAutoVerify()} == true.
+     * Effectively {@link #collectAllWebDomains(AndroidPackage)}, but requires {@link
+     * IntentFilter#getAutoVerify()} == true.
      */
     @NonNull
     public ArraySet<String> collectAutoVerifyDomains(@NonNull AndroidPackage pkg) {
@@ -100,24 +102,21 @@
         boolean restrictDomains =
                 DomainVerificationUtils.isChangeEnabled(mPlatformCompat, pkg, RESTRICT_DOMAINS);
 
-        ArraySet<String> domains = new ArraySet<>();
-
         if (restrictDomains) {
-            collectDomains(domains, pkg, checkAutoVerify);
+            return collectDomainsInternal(pkg, checkAutoVerify);
         } else {
-            collectDomainsLegacy(domains, pkg, checkAutoVerify);
+            return collectDomainsLegacy(pkg, checkAutoVerify);
         }
-
-        return domains;
     }
 
-    /** @see #RESTRICT_DOMAINS */
-    private void collectDomainsLegacy(@NonNull Set<String> domains,
-            @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+    /**
+     * @see #RESTRICT_DOMAINS
+     */
+    private ArraySet<String> collectDomainsLegacy(@NonNull AndroidPackage pkg,
+            boolean checkAutoVerify) {
         if (!checkAutoVerify) {
             // Per-domain user selection state doesn't have a V1 equivalent on S, so just use V2
-            collectDomains(domains, pkg, false);
-            return;
+            return collectDomainsInternal(pkg, false);
         }
 
         List<ParsedActivity> activities = pkg.getActivities();
@@ -140,39 +139,54 @@
             }
 
             if (!needsAutoVerify) {
-                return;
+                return new ArraySet<>();
             }
         }
 
-        for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+        ArraySet<String> domains = new ArraySet<>();
+        int totalSize = 0;
+        boolean underMaxSize = true;
+        for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize;
+                activityIndex++) {
             ParsedActivity activity = activities.get(activityIndex);
             List<ParsedIntentInfo> intents = activity.getIntents();
             int intentsSize = intents.size();
-            for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+            for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
                 ParsedIntentInfo intent = intents.get(intentIndex);
                 if (intent.handlesWebUris(false)) {
                     int authorityCount = intent.countDataAuthorities();
                     for (int index = 0; index < authorityCount; index++) {
                         String host = intent.getDataAuthority(index).getHost();
                         if (isValidHost(host)) {
+                            totalSize += byteSizeOf(host);
+                            underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
                             domains.add(host);
                         }
                     }
                 }
             }
         }
+
+        return domains;
     }
 
-    /** @see #RESTRICT_DOMAINS */
-    private void collectDomains(@NonNull Set<String> domains,
-            @NonNull AndroidPackage pkg, boolean checkAutoVerify) {
+    /**
+     * @see #RESTRICT_DOMAINS
+     */
+    private ArraySet<String> collectDomainsInternal(@NonNull AndroidPackage pkg,
+            boolean checkAutoVerify) {
+        ArraySet<String> domains = new ArraySet<>();
+        int totalSize = 0;
+        boolean underMaxSize = true;
+
         List<ParsedActivity> activities = pkg.getActivities();
         int activitiesSize = activities.size();
-        for (int activityIndex = 0; activityIndex < activitiesSize; activityIndex++) {
+        for (int activityIndex = 0; activityIndex < activitiesSize && underMaxSize;
+                activityIndex++) {
             ParsedActivity activity = activities.get(activityIndex);
             List<ParsedIntentInfo> intents = activity.getIntents();
             int intentsSize = intents.size();
-            for (int intentIndex = 0; intentIndex < intentsSize; intentIndex++) {
+            for (int intentIndex = 0; intentIndex < intentsSize && underMaxSize; intentIndex++) {
                 ParsedIntentInfo intent = intents.get(intentIndex);
                 if (checkAutoVerify && !intent.getAutoVerify()) {
                     continue;
@@ -198,14 +212,27 @@
                 //  app developer by declaring a separate intent-filter. This may not be worth
                 //  fixing.
                 int authorityCount = intent.countDataAuthorities();
-                for (int index = 0; index < authorityCount; index++) {
+                for (int index = 0; index < authorityCount && underMaxSize; index++) {
                     String host = intent.getDataAuthority(index).getHost();
                     if (isValidHost(host)) {
+                        totalSize += byteSizeOf(host);
+                        underMaxSize = totalSize < MAX_DOMAINS_BYTE_SIZE;
                         domains.add(host);
                     }
                 }
             }
         }
+
+        return domains;
+    }
+
+    /**
+     * Ballpark the size of domains to avoid a ridiculous amount of domains that could slow
+     * down client-server communication.
+     */
+    private int byteSizeOf(String string) {
+        // Use the same method from core for the data objects so that restrictions are consistent
+        return android.content.pm.verify.domain.DomainVerificationUtils.estimatedByteSizeOf(string);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
index 275dd053..ed37fa0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationEnforcer.java
@@ -185,6 +185,29 @@
         return !mCallback.filterAppAccess(packageName, callingUid, targetUserId);
     }
 
+    /**
+     * Querying for the owners of a domain. Because this API cannot filter the returned list of
+     * packages, enforces {@link android.Manifest.permission.QUERY_ALL_PACKAGES}, but also enforces
+     * {@link android.Manifest.permission.INTERACT_ACROSS_USERS} because each user has a different
+     * state.
+     */
+    public void assertOwnerQuerent(int callingUid, @UserIdInt int callingUserId,
+            @UserIdInt int targetUserId) {
+        final int callingPid = Binder.getCallingPid();
+        if (callingUserId != targetUserId) {
+            mContext.enforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS,
+                    callingPid, callingUid, "Caller is not allowed to query other users");
+        }
+
+        mContext.enforcePermission(android.Manifest.permission.QUERY_ALL_PACKAGES,
+                callingPid, callingUid, "Caller " + callingUid + " does not hold "
+                        + android.Manifest.permission.QUERY_ALL_PACKAGES);
+
+        mContext.enforcePermission(
+                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION,
+                callingPid, callingUid, "Caller is not allowed to query user selections");
+    }
+
     public interface Callback {
         /**
          * @return true if access to the given package should be filtered and the method failed as
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
index 8aa6337..6f28107 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerStub.java
@@ -20,13 +20,14 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.verify.domain.DomainOwner;
+import android.content.pm.verify.domain.DomainSet;
+import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager.InvalidDomainSetException;
 import android.content.pm.verify.domain.DomainVerificationManagerImpl;
-import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationUserSelection;
 import android.content.pm.verify.domain.IDomainVerificationManager;
 import android.os.ServiceSpecificException;
-import android.util.ArraySet;
 
 import java.util.List;
 import java.util.UUID;
@@ -61,11 +62,11 @@
     }
 
     @Override
-    public void setDomainVerificationStatus(String domainSetId, List<String> domains,
+    public void setDomainVerificationStatus(String domainSetId, @NonNull DomainSet domainSet,
             int state) {
         try {
             mService.setDomainVerificationStatus(UUID.fromString(domainSetId),
-                            new ArraySet<>(domains), state);
+                    domainSet.getDomains(), state);
         } catch (Exception e) {
             throw rethrow(e);
         }
@@ -82,11 +83,11 @@
     }
 
     @Override
-    public void setDomainVerificationUserSelection(String domainSetId, List<String> domains,
+    public void setDomainVerificationUserSelection(String domainSetId, @NonNull DomainSet domainSet,
             boolean enabled, @UserIdInt int userId) {
         try {
             mService.setDomainVerificationUserSelection(UUID.fromString(domainSetId),
-                            new ArraySet<>(domains), enabled, userId);
+                    domainSet.getDomains(), enabled, userId);
         } catch (Exception e) {
             throw rethrow(e);
         }
@@ -103,6 +104,17 @@
         }
     }
 
+    @Nullable
+    @Override
+    public List<DomainOwner> getOwnersForDomain(@NonNull String domain,
+            @UserIdInt int userId) {
+        try {
+            return mService.getOwnersForDomain(domain, userId);
+        } catch (Exception e) {
+            throw rethrow(e);
+        }
+    }
+
     private RuntimeException rethrow(Exception exception) throws RuntimeException {
         if (exception instanceof InvalidDomainSetException) {
             int packedErrorCode = DomainVerificationManagerImpl.ERROR_INVALID_DOMAIN_SET;
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 8dcb45f..b58c1ff 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -17,6 +17,7 @@
 package com.android.server.pm.verify.domain;
 
 import static java.util.Collections.emptyList;
+import static java.util.Collections.emptySet;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -30,6 +31,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.verify.domain.DomainOwner;
 import android.content.pm.verify.domain.DomainVerificationInfo;
 import android.content.pm.verify.domain.DomainVerificationManager;
 import android.content.pm.verify.domain.DomainVerificationState;
@@ -291,6 +293,8 @@
             throws InvalidDomainSetException, NameNotFoundException {
         mEnforcer.assertApprovedVerifier(callingUid, mProxy);
         synchronized (mLock) {
+            List<String> verifiedDomains = new ArrayList<>();
+
             DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
                     true /* forAutoVerify */, callingUid, null /* userId */);
             ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
@@ -301,8 +305,17 @@
                     continue;
                 }
 
+                if (DomainVerificationManager.isStateVerified(state)) {
+                    verifiedDomains.add(domain);
+                }
+
                 stateMap.put(domain, state);
             }
+
+            int size = verifiedDomains.size();
+            for (int index = 0; index < size; index++) {
+                removeUserSelectionsForDomain(verifiedDomains.get(index));
+            }
         }
 
         mConnection.scheduleWriteSettings();
@@ -387,6 +400,20 @@
         }
     }
 
+    private void removeUserSelectionsForDomain(@NonNull String domain) {
+        synchronized (mLock) {
+            final int size = mAttachedPkgStates.size();
+            for (int index = 0; index < size; index++) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                SparseArray<DomainVerificationUserState> array = pkgState.getUserSelectionStates();
+                int arraySize = array.size();
+                for (int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) {
+                    array.valueAt(arrayIndex).removeHost(domain);
+                }
+            }
+        }
+    }
+
     @Override
     public void setDomainVerificationLinkHandlingAllowed(@NonNull String packageName,
             boolean allowed) throws NameNotFoundException {
@@ -470,19 +497,59 @@
                         InvalidDomainSetException.REASON_ID_INVALID);
             }
 
+            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
+                    false /* forAutoVerify */, callingUid, userId);
+            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
+
+            // Disable other packages if approving this one. Note that this check is only done for
+            // enabling. This allows an escape hatch in case multiple packages somehow get selected.
+            // They can be disabled without blocking in a circular dependency.
             if (enabled) {
+                // Cache the approved packages from the 1st pass because the search is expensive
+                ArrayMap<String, List<String>> domainToApprovedPackages = new ArrayMap<>();
+
                 for (String domain : domains) {
-                    if (!getApprovedPackages(domain, userId, APPROVAL_LEVEL_LEGACY_ALWAYS + 1,
-                            mConnection::getPackageSettingLocked).first.isEmpty()) {
+                    if (userState.getEnabledHosts().contains(domain)) {
+                        continue;
+                    }
+
+                    Pair<List<String>, Integer> packagesToLevel = getApprovedPackages(domain,
+                            userId, APPROVAL_LEVEL_NONE + 1, mConnection::getPackageSettingLocked);
+                    int highestApproval = packagesToLevel.second;
+                    if (highestApproval > APPROVAL_LEVEL_SELECTION) {
                         throw new InvalidDomainSetException(domainSetId, null,
                                 InvalidDomainSetException.REASON_UNABLE_TO_APPROVE);
                     }
+
+                    domainToApprovedPackages.put(domain, packagesToLevel.first);
+                }
+
+                // The removal for other packages must be done in a 2nd pass after it's determined
+                // that no higher priority owners exist for all of the domains in the set.
+                int mapSize = domainToApprovedPackages.size();
+                for (int mapIndex = 0; mapIndex < mapSize; mapIndex++) {
+                    String domain = domainToApprovedPackages.keyAt(mapIndex);
+                    List<String> approvedPackages = domainToApprovedPackages.valueAt(mapIndex);
+                    int approvedSize = approvedPackages.size();
+                    for (int approvedIndex = 0; approvedIndex < approvedSize; approvedIndex++) {
+                        String approvedPackage = approvedPackages.get(approvedIndex);
+                        DomainVerificationPkgState approvedPkgState =
+                                mAttachedPkgStates.get(approvedPackage);
+                        if (approvedPkgState == null) {
+                            continue;
+                        }
+
+                        DomainVerificationUserState approvedUserState =
+                                approvedPkgState.getUserSelectionState(userId);
+                        if (approvedUserState == null) {
+                            continue;
+                        }
+
+                        approvedUserState.removeHost(domain);
+                    }
                 }
             }
 
-            DomainVerificationPkgState pkgState = getAndValidateAttachedLocked(domainSetId, domains,
-                    false /* forAutoVerify */, callingUid, userId);
-            DomainVerificationUserState userState = pkgState.getOrCreateUserSelectionState(userId);
             if (enabled) {
                 userState.addHosts(domains);
             } else {
@@ -600,32 +667,113 @@
                 throw DomainVerificationUtils.throwPackageUnavailable(packageName);
             }
 
-            ArrayMap<String, Boolean> hostToUserSelectionMap = new ArrayMap<>();
+            ArraySet<String> webDomains = mCollector.collectAllWebDomains(pkg);
+            int webDomainsSize = webDomains.size();
 
-            ArraySet<String> domains = mCollector.collectAllWebDomains(pkg);
-            int domainsSize = domains.size();
-            for (int index = 0; index < domainsSize; index++) {
-                hostToUserSelectionMap.put(domains.valueAt(index), false);
-            }
-
+            Map<String, Integer> domains = new ArrayMap<>(webDomainsSize);
+            ArrayMap<String, Integer> stateMap = pkgState.getStateMap();
             DomainVerificationUserState userState = pkgState.getUserSelectionState(userId);
-            boolean linkHandlingAllowed = true;
-            if (userState != null) {
-                linkHandlingAllowed = userState.isLinkHandlingAllowed();
-                ArraySet<String> enabledHosts = userState.getEnabledHosts();
-                int hostsSize = enabledHosts.size();
-                for (int index = 0; index < hostsSize; index++) {
-                    hostToUserSelectionMap.put(enabledHosts.valueAt(index), true);
+            Set<String> enabledHosts = userState == null ? emptySet() : userState.getEnabledHosts();
+
+            for (int index = 0; index < webDomainsSize; index++) {
+                String host = webDomains.valueAt(index);
+                Integer state = stateMap.get(host);
+
+                int domainState;
+                if (state != null && DomainVerificationManager.isStateVerified(state)) {
+                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED;
+                } else if (enabledHosts.contains(host)) {
+                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED;
+                } else {
+                    domainState = DomainVerificationUserSelection.DOMAIN_STATE_NONE;
                 }
+
+                domains.put(host, domainState);
             }
 
+            boolean linkHandlingAllowed = userState == null || userState.isLinkHandlingAllowed();
+
             return new DomainVerificationUserSelection(pkgState.getId(), packageName,
-                    UserHandle.of(userId), linkHandlingAllowed, hostToUserSelectionMap);
+                    UserHandle.of(userId), linkHandlingAllowed, domains);
         }
     }
 
     @NonNull
     @Override
+    public List<DomainOwner> getOwnersForDomain(@NonNull String domain) {
+        return getOwnersForDomain(domain, mConnection.getCallingUserId());
+    }
+
+    public List<DomainOwner> getOwnersForDomain(@NonNull String domain, @UserIdInt int userId) {
+        mEnforcer.assertOwnerQuerent(mConnection.getCallingUid(), mConnection.getCallingUserId(),
+                userId);
+
+        SparseArray<List<String>> levelToPackages = new SparseArray<>();
+
+        // First, collect the raw approval level values
+        synchronized (mLock) {
+            final int size = mAttachedPkgStates.size();
+            for (int index = 0; index < size; index++) {
+                DomainVerificationPkgState pkgState = mAttachedPkgStates.valueAt(index);
+                String packageName = pkgState.getPackageName();
+                PackageSetting pkgSetting = mConnection.getPackageSettingLocked(packageName);
+                if (pkgSetting == null) {
+                    continue;
+                }
+
+                int level = approvalLevelForDomain(pkgSetting, domain, userId, domain);
+                if (level <= APPROVAL_LEVEL_NONE) {
+                    continue;
+                }
+                List<String> list = levelToPackages.get(level);
+                if (list == null) {
+                    list = new ArrayList<>();
+                    levelToPackages.put(level, list);
+                }
+                list.add(packageName);
+            }
+        }
+
+        final int size = levelToPackages.size();
+        if (size == 0) {
+            return emptyList();
+        }
+
+        // Then sort them ascending by first installed time, with package name as the tie breaker
+        for (int index = 0; index < size; index++) {
+            levelToPackages.valueAt(index).sort((first, second) -> {
+                PackageSetting firstPkgSetting = mConnection.getPackageSettingLocked(first);
+                PackageSetting secondPkgSetting = mConnection.getPackageSettingLocked(second);
+
+                long firstInstallTime =
+                        firstPkgSetting == null ? -1L : firstPkgSetting.getFirstInstallTime();
+                long secondInstallTime =
+                        secondPkgSetting == null ? -1L : secondPkgSetting.getFirstInstallTime();
+
+                if (firstInstallTime != secondInstallTime) {
+                    return (int) (firstInstallTime - secondInstallTime);
+                }
+
+                return first.compareToIgnoreCase(second);
+            });
+        }
+
+        List<DomainOwner> owners = new ArrayList<>();
+        for (int index = 0; index < size; index++) {
+            int level = levelToPackages.keyAt(index);
+            boolean overrideable = level <= APPROVAL_LEVEL_SELECTION;
+            List<String> packages = levelToPackages.valueAt(index);
+            int packagesSize = packages.size();
+            for (int packageIndex = 0; packageIndex < packagesSize; packageIndex++) {
+                owners.add(new DomainOwner(packages.get(packageIndex), overrideable));
+            }
+        }
+
+        return owners;
+    }
+
+    @NonNull
+    @Override
     public UUID generateNewId() {
         // TODO(b/159952358): Domain set ID collisions
         return UUID.randomUUID();
@@ -634,7 +782,7 @@
     @Override
     public void migrateState(@NonNull PackageSetting oldPkgSetting,
             @NonNull PackageSetting newPkgSetting) {
-        String pkgName = newPkgSetting.name;
+        String pkgName = newPkgSetting.getName();
         boolean sendBroadcast;
 
         synchronized (mLock) {
@@ -730,7 +878,7 @@
         //  gains or loses all domains.
 
         UUID domainSetId = newPkgSetting.getDomainSetId();
-        String pkgName = newPkgSetting.name;
+        String pkgName = newPkgSetting.getName();
 
         boolean sendBroadcast = true;
 
@@ -1347,7 +1495,7 @@
     @Override
     public int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull Intent intent,
             @PackageManager.ResolveInfoFlags int resolveInfoFlags, @UserIdInt int userId) {
-        String packageName = pkgSetting.name;
+        String packageName = pkgSetting.getName();
         if (!DomainVerificationUtils.isDomainVerificationIntent(intent, resolveInfoFlags)) {
             if (DEBUG_APPROVAL) {
                 debugApproval(packageName, intent, userId, false, "not valid intent");
@@ -1364,7 +1512,7 @@
      */
     private int approvalLevelForDomain(@NonNull PackageSetting pkgSetting, @NonNull String host,
             @UserIdInt int userId, @NonNull Object debugObject) {
-        String packageName = pkgSetting.name;
+        String packageName = pkgSetting.getName();
         final AndroidPackage pkg = pkgSetting.getPkg();
 
         // Should never be null, but if it is, skip this and assume that v2 is enabled
diff --git a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
index 2246864..8fbb33a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
+++ b/services/core/java/com/android/server/pm/verify/domain/models/DomainVerificationUserState.java
@@ -58,6 +58,11 @@
         return this;
     }
 
+    public DomainVerificationUserState removeHost(String host) {
+        mEnabledHosts.remove(host);
+        return this;
+    }
+
     public DomainVerificationUserState removeHosts(@NonNull ArraySet<String> newHosts) {
         mEnabledHosts.removeAll(newHosts);
         return this;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dd9619a..f40f4a9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -40,8 +40,11 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO;
 import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO;
 import static android.content.Intent.ACTION_MAIN;
@@ -209,6 +212,7 @@
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
 import static com.android.server.wm.WindowManagerService.LETTERBOX_BACKGROUND_SOLID_COLOR;
+import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY;
@@ -557,7 +561,7 @@
 
     /**
      * The precomputed display insets for resolving configuration. It will be non-null if
-     * {@link #shouldUseSizeCompatMode} returns {@code true}.
+     * {@link #shouldCreateCompatDisplayInsets} returns {@code true}.
      */
     private CompatDisplayInsets mCompatDisplayInsets;
 
@@ -648,6 +652,18 @@
      */
     private Rect mSizeCompatBounds;
 
+    // Whether this activity is in size compatibility mode because its bounds don't fit in parent
+    // naturally.
+    private boolean mInSizeCompatModeForBounds = false;
+
+    // Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
+    // orientation then aspect ratio restrictions are also already respected.
+    // This happens when an activity has fixed orientation which doesn't match orientation of the
+    // parent because a display is ignoring orientation request or fixed to user rotation.
+    // See WindowManagerService#getIgnoreOrientationRequest and
+    // WindowManagerService#getFixedToUserRotation for more context.
+    private boolean mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+
     // activity is not displayed?
     // TODO: rename to mNoDisplay
     @VisibleForTesting
@@ -1296,9 +1312,6 @@
             // TODO(b/36505427): Maybe this call should be moved inside
             // updateOverrideConfiguration()
             newTask.updateOverrideConfigurationFromLaunchBounds();
-            // Make sure override configuration is up-to-date before using to create window
-            // controller.
-            updateSizeCompatMode();
             // When an activity is started directly into a split-screen fullscreen root task, we
             // need to update the initial multi-window modes so that the callbacks are scheduled
             // correctly when the user leaves that mode.
@@ -6718,12 +6731,8 @@
         }
 
         if (onDescendantOrientationChanged(this)) {
-            // The app is just becoming visible, and the parent Task has updated with the
-            // orientation request. Update the size compat mode.
-            updateSizeCompatMode();
-            // WM Shell can override WM Core positioning (e.g. for letterboxing) so ensure
-            // that WM Shell is called when an activity becomes visible. Without this, WM Core
-            // will handle positioning instead of WM Shell when an app is reopened.
+            // WM Shell can show additional UI elements, e.g. a restart button for size compat mode
+            // so ensure that WM Shell is called when an activity becomes visible.
             task.dispatchTaskInfoChangedIfNeeded(/* force= */ true);
         }
     }
@@ -6788,7 +6797,10 @@
      *         density than its parent or its bounds don't fit in parent naturally.
      */
     boolean inSizeCompatMode() {
-        if (mCompatDisplayInsets == null || !shouldUseSizeCompatMode()
+        if (mInSizeCompatModeForBounds) {
+            return true;
+        }
+        if (mCompatDisplayInsets == null || !shouldCreateCompatDisplayInsets()
                 // The orientation is different from parent when transforming.
                 || isFixedRotationTransforming()) {
             return false;
@@ -6798,70 +6810,30 @@
             // The app bounds hasn't been computed yet.
             return false;
         }
-
         final Configuration parentConfig = getParent().getConfiguration();
         // Although colorMode, screenLayout, smallestScreenWidthDp are also fixed, generally these
         // fields should be changed with density and bounds, so here only compares the most
         // significant field.
-        if (parentConfig.densityDpi != getConfiguration().densityDpi) {
-            return true;
-        }
-
-        final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
-        final int appWidth = appBounds.width();
-        final int appHeight = appBounds.height();
-        final int parentAppWidth = parentAppBounds.width();
-        final int parentAppHeight = parentAppBounds.height();
-        if (parentAppWidth == appWidth && parentAppHeight == appHeight) {
-            // Matched the parent bounds.
-            return false;
-        }
-        if (parentAppWidth > appWidth && parentAppHeight > appHeight) {
-            // Both sides are smaller than the parent.
-            return true;
-        }
-        if (parentAppWidth < appWidth || parentAppHeight < appHeight) {
-            // One side is larger than the parent.
-            return true;
-        }
-
-        // The rest of the condition is that only one side is smaller than the parent, but it still
-        // needs to exclude the cases where the size is limited by the fixed aspect ratio.
-        if (info.maxAspectRatio > 0) {
-            final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
-                    / Math.min(appWidth, appHeight);
-            if (aspectRatio >= info.maxAspectRatio) {
-                // The current size has reached the max aspect ratio.
-                return false;
-            }
-        }
-        if (info.minAspectRatio > 0) {
-            // The activity should have at least the min aspect ratio, so this checks if the parent
-            // still has available space to provide larger aspect ratio.
-            final float parentAspectRatio = (0.5f + Math.max(parentAppWidth, parentAppHeight))
-                    / Math.min(parentAppWidth, parentAppHeight);
-            if (parentAspectRatio <= info.minAspectRatio) {
-                // The long side has reached the parent.
-                return false;
-            }
-        }
-        return true;
+        return parentConfig.densityDpi != getConfiguration().densityDpi;
     }
 
     /**
      * Indicates the activity will keep the bounds and screen configuration when it was first
      * launched, no matter how its parent changes.
      *
+     * <p>If {@true}, then {@link CompatDisplayInsets} will be created in {@link
+     * #resolveOverrideConfiguration} to "freeze" activity bounds and insets.
+     *
      * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
      *         aspect ratio.
      */
-    boolean shouldUseSizeCompatMode() {
+    boolean shouldCreateCompatDisplayInsets() {
         if (info.supportsSizeChanges() != ActivityInfo.SIZE_CHANGES_UNSUPPORTED) {
             return false;
         }
         if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
             final ActivityRecord root = task != null ? task.getRootActivity() : null;
-            if (root != null && root != this && !root.shouldUseSizeCompatMode()) {
+            if (root != null && root != this && !root.shouldCreateCompatDisplayInsets()) {
                 // If the root activity doesn't use size compatibility mode, the activities above
                 // are forced to be the same for consistent visual appearance.
                 return false;
@@ -6883,25 +6855,11 @@
     }
 
     // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
-    private void updateSizeCompatMode() {
-        if (mCompatDisplayInsets != null || !shouldUseSizeCompatMode()) {
+    private void updateCompatDisplayInsets(@Nullable Rect fixedOrientationBounds) {
+        if (mCompatDisplayInsets != null || !shouldCreateCompatDisplayInsets()) {
             // The override configuration is set only once in size compatibility mode.
             return;
         }
-        final Configuration parentConfig = getParent().getConfiguration();
-        if (!hasProcess() && !isConfigurationCompatible(parentConfig)) {
-            // Don't compute when launching in fullscreen and the fixed orientation is not the
-            // current orientation. It is more accurately to compute the override bounds from
-            // the updated configuration after the fixed orientation is applied.
-            return;
-        }
-
-        if (task == null || (!handlesOrientationChangeFromDescendant()
-                && task.getLastTaskBoundsComputeActivity() != this)) {
-            // Don't compute when Task hasn't computed its bounds for this app, because the Task can
-            // be letterboxed, and its bounds may not be accurate until then.
-            return;
-        }
 
         Configuration overrideConfig = getRequestedOverrideConfiguration();
         final Configuration fullConfig = getConfiguration();
@@ -6924,17 +6882,18 @@
         }
 
         // The role of CompatDisplayInsets is like the override bounds.
-        mCompatDisplayInsets = new CompatDisplayInsets(mDisplayContent, this);
+        mCompatDisplayInsets =
+                new CompatDisplayInsets(mDisplayContent, this, fixedOrientationBounds);
     }
 
     @VisibleForTesting
     void clearSizeCompatMode() {
+        mInSizeCompatModeForBounds = false;
         mSizeCompatScale = 1f;
         mSizeCompatBounds = null;
         mCompatDisplayInsets = null;
 
-        // Recompute from Task because letterbox can also happen on Task level.
-        task.onRequestedOverrideConfigurationChanged(task.getRequestedOverrideConfiguration());
+        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
     }
 
     @Override
@@ -6969,23 +6928,41 @@
             mTmpConfig.updateFrom(resolvedConfig);
             newParentConfiguration = mTmpConfig;
         }
+
+        final int windowingMode = getWindowingMode();
+        // TODO(b/181207944): Consider removing the if condition and always run
+        // resolveFixedOrientationConfiguration() since this should be applied for all cases.
+        if (isSplitScreenWindowingMode(windowingMode)
+                || windowingMode == WINDOWING_MODE_MULTI_WINDOW
+                || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            resolveFixedOrientationConfiguration(newParentConfiguration);
+        }
+        final Rect fixedOrientationBounds = isLetterboxedForFixedOrientationAndAspectRatio()
+                ? new Rect(resolvedConfig.windowConfiguration.getBounds()) : null;
+
         if (mCompatDisplayInsets != null) {
             resolveSizeCompatModeConfiguration(newParentConfiguration);
-        } else {
-            if (inMultiWindowMode()) {
-                // We ignore activities' requested orientation in multi-window modes. Task level may
-                // take them into consideration when calculating bounds.
-                resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
-                // If the activity has requested override bounds, the configuration needs to be
-                // computed accordingly.
-                if (!matchParentBounds()) {
-                    task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
-                }
-            } else {
-                resolveFullscreenConfiguration(newParentConfiguration);
+        } else if (inMultiWindowMode()) {
+            // We ignore activities' requested orientation in multi-window modes. They may be
+            // taken into consideration in resolveFixedOrientationConfiguration call above.
+            resolvedConfig.orientation = Configuration.ORIENTATION_UNDEFINED;
+            // If the activity has requested override bounds, the configuration needs to be
+            // computed accordingly.
+            if (!matchParentBounds()) {
+                task.computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
             }
+        // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+        // are already calculated in resolveFixedOrientationConfiguration.
+        } else if (!isLetterboxedForFixedOrientationAndAspectRatio()) {
+            resolveFullscreenConfiguration(newParentConfiguration);
         }
 
+        if (mVisibleRequested) {
+            updateCompatDisplayInsets(fixedOrientationBounds);
+        }
+
+        // TODO(b/175212232): Consolidate position logic from each "resolve" method above here.
+
         // Assign configuration sequence number into hierarchy because there is a different way than
         // ensureActivityConfiguration() in this class that uses configuration in WindowState during
         // layout traversals.
@@ -6994,6 +6971,109 @@
     }
 
     /**
+     * Whether this activity is letterboxed for fixed orientation. If letterboxed due to fixed
+     * orientation then aspect ratio restrictions are also already respected.
+     *
+     * <p>This happens when an activity has fixed orientation which doesn't match orientation of the
+     * parent because a display setting 'ignoreOrientationRequest' is set to true. See {@link
+     * WindowManagerService#getIgnoreOrientationRequest} for more context.
+     */
+    boolean isLetterboxedForFixedOrientationAndAspectRatio() {
+        return mIsLetterboxedForFixedOrientationAndAspectRatio;
+    }
+
+    /**
+     * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
+     * change and the requested orientation is different from the parent.
+     *
+     * <p>If letterboxed due to fixed orientation then aspect ratio restrictions are also applied
+     * in this methiod.
+     */
+    private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig) {
+        mIsLetterboxedForFixedOrientationAndAspectRatio = false;
+        if (handlesOrientationChangeFromDescendant()) {
+            // No need to letterbox because of fixed orientation. Display will handle
+            // fixed-orientation requests.
+            return;
+        }
+
+        final Rect resolvedBounds =
+                getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+        final int parentOrientation = newParentConfig.orientation;
+
+        // If the activity requires a different orientation (either by override or activityInfo),
+        // make it fit the available bounds by scaling down its bounds.
+        final int forcedOrientation = getRequestedConfigurationOrientation();
+        if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
+            return;
+        }
+
+        if (mCompatDisplayInsets != null && !mCompatDisplayInsets.mIsInFixedOrientationLetterbox) {
+            // App prefers to keep its original size.
+            // If the size compat is from previous fixed orientation letterboxing, we may want to
+            // have fixed orientation letterbox again, otherwise it will show the size compat
+            // restart button even if the restart bounds will be the same.
+            return;
+        }
+
+        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+        final int parentWidth = parentBounds.width();
+        final int parentHeight = parentBounds.height();
+        float aspect = Math.max(parentWidth, parentHeight)
+                / (float) Math.min(parentWidth, parentHeight);
+
+        // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
+        // order to use the extra available space.
+        final float maxAspectRatio = info.maxAspectRatio;
+        final float minAspectRatio = info.minAspectRatio;
+        if (aspect > maxAspectRatio && maxAspectRatio != 0) {
+            aspect = maxAspectRatio;
+        } else if (aspect < minAspectRatio) {
+            aspect = minAspectRatio;
+        }
+
+        // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
+        // TODO(b/175212232): Rename getTaskLetterboxAspectRatio and all related methods since fixed
+        // orientation letterbox is on the activity level now.
+        final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
+        // Activity min/max aspect ratio restrictions will be respected by the activity-level
+        // letterboxing (size-compat mode). Therefore this override can control the maximum screen
+        // area that can be occupied by the app in the letterbox mode.
+        aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
+                ? letterboxAspectRatioOverride : aspect;
+
+        // Store the current bounds to be able to revert to size compat mode values below if needed.
+        Rect mTmpFullBounds = new Rect(resolvedBounds);
+        if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+            final int height = (int) Math.rint(parentWidth / aspect);
+            final int top = parentBounds.centerY() - height / 2;
+            resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
+        } else {
+            final int width = (int) Math.rint(parentHeight / aspect);
+            final int left = parentBounds.centerX() - width / 2;
+            resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
+        }
+
+        if (mCompatDisplayInsets != null) {
+            mCompatDisplayInsets.getBoundsByRotation(
+                    mTmpBounds, newParentConfig.windowConfiguration.getRotation());
+            if (resolvedBounds.width() != mTmpBounds.width()
+                    || resolvedBounds.height() != mTmpBounds.height()) {
+                // The app shouldn't be resized, we only do fixed orientation letterboxing if the
+                // compat bounds are also from the same fixed orientation letterbox. Otherwise,
+                // clear the fixed orientation bounds to show app in size compat mode.
+                resolvedBounds.set(mTmpFullBounds);
+                return;
+            }
+        }
+
+        // Calculate app bounds using fixed orientation bounds because they will be needed later
+        // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
+        task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
+        mIsLetterboxedForFixedOrientationAndAspectRatio = true;
+    }
+
+    /**
      * Resolves the configuration of activity in fullscreen mode. If the bounds are restricted by
      * aspect ratio, the position will be centered horizontally in parent's app bounds to balance
      * the visual appearance. The policy of aspect ratio has higher priority than the requested
@@ -7037,6 +7117,18 @@
     private void resolveSizeCompatModeConfiguration(Configuration newParentConfiguration) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+
+        // When an activity needs to be letterboxed because of fixed orientation, use fixed
+        // orientation bounds (stored in resolved bounds) instead of parent bounds since the
+        // activity will be displayed within them even if it is in size compat mode. They should be
+        // saved here before resolved bounds are overridden below.
+        final Rect containerBounds = isLetterboxedForFixedOrientationAndAspectRatio()
+                ? new Rect(resolvedBounds)
+                : newParentConfiguration.windowConfiguration.getBounds();
+        final Rect containerAppBounds = isLetterboxedForFixedOrientationAndAspectRatio()
+                ? new Rect(getResolvedOverrideConfiguration().windowConfiguration.getAppBounds())
+                : newParentConfiguration.windowConfiguration.getAppBounds();
+
         final int requestedOrientation = getRequestedConfigurationOrientation();
         final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
         final int orientation = orientationRequested
@@ -7095,7 +7187,7 @@
         // Below figure is an example that puts an activity which was launched in a larger container
         // into a smaller container.
         //   The outermost rectangle is the real display bounds.
-        //   "@" is the parent app bounds.
+        //   "@" is the container app bounds (parent bounds or fixed orientation bouds)
         //   "#" is the {@code resolvedBounds} that applies to application.
         //   "*" is the {@code mSizeCompatBounds} that used to show on screen if scaled.
         // ------------------------------
@@ -7111,19 +7203,18 @@
         // The application is still layouted in "#" since it was launched, and it will be visually
         // scaled and positioned to "*".
 
+        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
+
         // Calculates the scale and offset to horizontal center the size compatibility bounds into
         // the region which is available to application.
-        final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
-        final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds();
-        final Rect resolvedAppBounds = resolvedConfig.windowConfiguration.getAppBounds();
         final int contentW = resolvedAppBounds.width();
         final int contentH = resolvedAppBounds.height();
-        final int viewportW = parentAppBounds.width();
-        final int viewportH = parentAppBounds.height();
+        final int viewportW = containerAppBounds.width();
+        final int viewportH = containerAppBounds.height();
         // Only allow to scale down.
         mSizeCompatScale = (contentW <= viewportW && contentH <= viewportH)
                 ? 1f : Math.min((float) viewportW / contentW, (float) viewportH / contentH);
-        final int screenTopInset = parentAppBounds.top - parentBounds.top;
+        final int screenTopInset = containerAppBounds.top - containerBounds.top;
         final boolean topNotAligned = screenTopInset != resolvedAppBounds.top - resolvedBounds.top;
         if (mSizeCompatScale != 1f || topNotAligned) {
             if (mSizeCompatBounds == null) {
@@ -7143,8 +7234,9 @@
         final int offsetX = getHorizontalCenterOffset(
                 (int) viewportW, (int) (contentW * mSizeCompatScale));
         // Above coordinates are in "@" space, now place "*" and "#" to screen space.
-        final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX;
-        final int screenPosY = parentBounds.top;
+        final int screenPosX = (fillContainer
+                ? containerBounds.left : containerAppBounds.left) + offsetX;
+        final int screenPosY = containerBounds.top;
         if (screenPosX != 0 || screenPosY != 0) {
             if (mSizeCompatBounds != null) {
                 mSizeCompatBounds.offset(screenPosX, screenPosY);
@@ -7154,6 +7246,52 @@
             final int dy = screenPosY - resolvedBounds.top;
             offsetBounds(resolvedConfig, dx, dy);
         }
+
+        mInSizeCompatModeForBounds =
+                isInSizeCompatModeForBounds(resolvedAppBounds, containerAppBounds);
+    }
+
+    private boolean isInSizeCompatModeForBounds(final Rect appBounds, final Rect containerBounds) {
+        final int appWidth = appBounds.width();
+        final int appHeight = appBounds.height();
+        final int containerAppWidth = containerBounds.width();
+        final int containerAppHeight = containerBounds.height();
+
+        if (containerAppWidth == appWidth && containerAppHeight == appHeight) {
+            // Matched the container bounds.
+            return false;
+        }
+        if (containerAppWidth > appWidth && containerAppHeight > appHeight) {
+            // Both sides are smaller than the container.
+            return true;
+        }
+        if (containerAppWidth < appWidth || containerAppHeight < appHeight) {
+            // One side is larger than the container.
+            return true;
+        }
+
+        // The rest of the condition is that only one side is smaller than the container, but it
+        // still needs to exclude the cases where the size is limited by the fixed aspect ratio.
+        if (info.maxAspectRatio > 0) {
+            final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
+                    / Math.min(appWidth, appHeight);
+            if (aspectRatio >= info.maxAspectRatio) {
+                // The current size has reached the max aspect ratio.
+                return false;
+            }
+        }
+        if (info.minAspectRatio > 0) {
+            // The activity should have at least the min aspect ratio, so this checks if the
+            // container still has available space to provide larger aspect ratio.
+            final float containerAspectRatio =
+                    (0.5f + Math.max(containerAppWidth, containerAppHeight))
+                            / Math.min(containerAppWidth, containerAppHeight);
+            if (containerAspectRatio <= info.minAspectRatio) {
+                // The long side has reached the parent.
+                return false;
+            }
+        }
+        return true;
     }
 
     /** @return The horizontal offset of putting the content in the center of viewport. */
@@ -7305,7 +7443,8 @@
         final Task rootTask = getRootTask();
         final float minAspectRatio = info.minAspectRatio;
 
-        if (task == null || rootTask == null || (inMultiWindowMode() && !shouldUseSizeCompatMode())
+        if (task == null || rootTask == null
+                || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
                 || (maxAspectRatio == 0 && minAspectRatio == 0)
                 || isInVrUiMode(getConfiguration())) {
             // We don't enforce aspect ratio if the activity task is in multiwindow unless it
@@ -7443,8 +7582,6 @@
         if (displayChanged) {
             mLastReportedDisplayId = newDisplayId;
         }
-        // TODO(b/36505427): Is there a better place to do this?
-        updateSizeCompatMode();
 
         // Short circuit: if the two full configurations are equal (the common case), then there is
         // nothing to do.  We test the full configuration instead of the global and merged override
@@ -7723,11 +7860,6 @@
         // Reset the existing override configuration so it can be updated according to the latest
         // configuration.
         clearSizeCompatMode();
-        if (mVisibleRequested) {
-            // Configuration will be ensured when becoming visible, so if it is already visible,
-            // then the manual update is needed.
-            updateSizeCompatMode();
-        }
 
         if (!attachedToProcess()) {
             return;
@@ -8134,8 +8266,11 @@
         private final int mHeight;
         /** Whether the {@link Task} windowingMode represents a floating window*/
         final boolean mIsFloating;
-        /** Whether the {@link Task} is letterboxed when the unresizable activity is first shown. */
-        final boolean mIsTaskLetterboxed;
+        /**
+         * Whether is letterboxed because of fixed orientation when the unresizable activity is
+         * first shown.
+         */
+        final boolean mIsInFixedOrientationLetterbox;
         /**
          * The nonDecorInsets for each rotation. Includes the navigation bar and cutout insets. It
          * is used to compute the appBounds.
@@ -8149,7 +8284,8 @@
         final Rect[] mStableInsets = new Rect[4];
 
         /** Constructs the environment to simulate the bounds behavior of the given container. */
-        CompatDisplayInsets(DisplayContent display, ActivityRecord container) {
+        CompatDisplayInsets(DisplayContent display, ActivityRecord container,
+                @Nullable Rect fixedOrientationBounds) {
             mIsFloating = container.getWindowConfiguration().tasksAreFloating();
             if (mIsFloating) {
                 final Rect containerBounds = container.getWindowConfiguration().getBounds();
@@ -8162,24 +8298,34 @@
                     mNonDecorInsets[rotation] = emptyRect;
                     mStableInsets[rotation] = emptyRect;
                 }
-                mIsTaskLetterboxed = false;
+                mIsInFixedOrientationLetterbox = false;
                 return;
             }
 
             final Task task = container.getTask();
-            mIsTaskLetterboxed = task != null && task.isTaskLetterboxed();
+
+            mIsInFixedOrientationLetterbox = fixedOrientationBounds != null;
 
             // Store the bounds of the Task for the non-resizable activity to use in size compat
             // mode so that the activity will not be resized regardless the windowing mode it is
             // currently in.
-            final WindowContainer filledContainer = task != null ? task : display;
-            final Point dimensions = getRotationZeroDimensions(filledContainer);
+            // When an activity needs to be letterboxed because of fixed orientation, use fixed
+            // orientation bounds instead of task bounds since the activity will be displayed
+            // within these even if it is in size compat mode.
+            final Rect filledContainerBounds = mIsInFixedOrientationLetterbox
+                    ? fixedOrientationBounds
+                    : task != null ? task.getBounds() : display.getBounds();
+            final int filledContainerRotation = task != null
+                    ? task.getConfiguration().windowConfiguration.getRotation()
+                    : display.getConfiguration().windowConfiguration.getRotation();
+            final Point dimensions = getRotationZeroDimensions(
+                    filledContainerBounds, filledContainerRotation);
             mWidth = dimensions.x;
             mHeight = dimensions.y;
 
             // Bounds of the filled container if it doesn't fill the display.
             final Rect unfilledContainerBounds =
-                    filledContainer.getBounds().equals(display.getBounds()) ? null : new Rect();
+                    filledContainerBounds.equals(display.getBounds()) ? null : new Rect();
             final DisplayPolicy policy = display.getDisplayPolicy();
             for (int rotation = 0; rotation < 4; rotation++) {
                 mNonDecorInsets[rotation] = new Rect();
@@ -8199,9 +8345,9 @@
                 // The insets is based on the display, but the container may be smaller than the
                 // display, so update the insets to exclude parts that are not intersected with the
                 // container.
-                unfilledContainerBounds.set(filledContainer.getBounds());
+                unfilledContainerBounds.set(filledContainerBounds);
                 display.rotateBounds(
-                        filledContainer.getConfiguration().windowConfiguration.getRotation(),
+                        filledContainerRotation,
                         rotation,
                         unfilledContainerBounds);
                 updateInsetsForBounds(unfilledContainerBounds, dw, dh, mNonDecorInsets[rotation]);
@@ -8214,9 +8360,7 @@
          * the display is rotated, we can calculate the bounds by rotating the dimensions.
          * @see #getBoundsByRotation
          */
-        private static Point getRotationZeroDimensions(WindowContainer container) {
-            final Rect bounds = container.getBounds();
-            final int rotation = container.getConfiguration().windowConfiguration.getRotation();
+        private static Point getRotationZeroDimensions(final Rect bounds, int rotation) {
             final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
             final int width = bounds.width();
             final int height = bounds.height();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f64f04c..32152ec 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2686,8 +2686,9 @@
         boolean newImmersiveMode = isImmersiveMode(win);
         if (oldImmersiveMode != newImmersiveMode) {
             mLastImmersiveMode = newImmersiveMode;
-            final String pkg = win.getOwningPackage();
-            mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
+            // The immersive confirmation window should be attached to the immersive window root.
+            final int rootDisplayAreaId = win.getRootDisplayArea().mFeatureId;
+            mImmersiveModeConfirmation.immersiveModeChangedLw(rootDisplayAreaId, newImmersiveMode,
                     mService.mPolicy.isUserSetupComplete(),
                     isNavBarEmpty(disableFlags));
         }
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 9286a46..567b6c2 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -19,9 +19,11 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
@@ -32,10 +34,12 @@
 import android.graphics.PixelFormat;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -43,6 +47,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.Gravity;
+import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -50,6 +55,7 @@
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -67,6 +73,8 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_SHOW_EVERY_TIME = false; // super annoying, use with caution
     private static final String CONFIRMED = "confirmed";
+    private static final int IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE =
+            WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
 
     private static boolean sConfirmed;
 
@@ -78,7 +86,15 @@
 
     private ClingWindowView mClingWindow;
     private long mPanicTime;
+    /** The last {@link WindowManager} that is used to add the confirmation window. */
+    @Nullable
     private WindowManager mWindowManager;
+    /**
+     * The WindowContext that is registered with {@link #mWindowManager} with options to specify the
+     * {@link RootDisplayArea} to attach the confirmation window.
+     */
+    @Nullable
+    private Context mWindowContext;
     // Local copy of vr mode enabled state, to avoid calling into VrManager with
     // the lock held.
     private boolean mVrModeEnabled;
@@ -132,7 +148,7 @@
         }
     }
 
-    void immersiveModeChangedLw(String pkg, boolean isImmersiveMode,
+    void immersiveModeChangedLw(int rootDisplayAreaId, boolean isImmersiveMode,
             boolean userSetupComplete, boolean navBarEmpty) {
         mHandler.removeMessages(H.SHOW);
         if (isImmersiveMode) {
@@ -143,7 +159,9 @@
                     && !navBarEmpty
                     && !UserManager.isDeviceInDemoMode(mContext)
                     && (mLockTaskState != LOCK_TASK_MODE_LOCKED)) {
-                mHandler.sendEmptyMessageDelayed(H.SHOW, mShowDelayMs);
+                final Message msg = mHandler.obtainMessage(H.SHOW);
+                msg.arg1 = rootDisplayAreaId;
+                mHandler.sendMessageDelayed(msg, mShowDelayMs);
             }
         } else {
             mHandler.sendEmptyMessage(H.HIDE);
@@ -175,7 +193,8 @@
     private void handleHide() {
         if (mClingWindow != null) {
             if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
-            getWindowManager().removeView(mClingWindow);
+            // We don't care which root display area the window manager is specifying for removal.
+            getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
             mClingWindow = null;
         }
     }
@@ -184,7 +203,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
+                IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
@@ -353,23 +372,57 @@
      * DO HOLD THE WINDOW MANAGER LOCK WHEN CALLING THIS METHOD
      * The reason why we add this method is to avoid the deadlock of WMG->WMS and WMS->WMG
      * when ImmersiveModeConfirmation object is created.
+     *
+     * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
+     *         confirmation window.
      */
-    private WindowManager getWindowManager() {
-        if (mWindowManager == null) {
-            mWindowManager = (WindowManager)
-                      mContext.getSystemService(Context.WINDOW_SERVICE);
+    private WindowManager getWindowManager(int rootDisplayAreaId) {
+        if (mWindowManager == null || mWindowContext == null) {
+            // Create window context to specify the RootDisplayArea
+            final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
+            mWindowContext = mContext.createWindowContext(
+                    IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+            mWindowManager = mWindowContext.getSystemService(WindowManager.class);
+            return mWindowManager;
         }
+
+        // Update the window context and window manager to specify the RootDisplayArea
+        final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
+        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+        try {
+            wms.registerWindowContextListener(mWindowContext.getWindowContextToken(),
+                    IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options);
+        }  catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+
         return mWindowManager;
     }
 
-    private void handleShow() {
+    /**
+     * Returns options that specify the {@link RootDisplayArea} to attach the confirmation window.
+     *         {@code null} if the {@code rootDisplayAreaId} is {@link FEATURE_UNDEFINED}.
+     */
+    @Nullable
+    private Bundle getOptionsForWindowContext(int rootDisplayAreaId) {
+        // In case we don't care which root display area the window manager is specifying.
+        if (rootDisplayAreaId == FEATURE_UNDEFINED) {
+            return null;
+        }
+
+        final Bundle options = new Bundle();
+        options.putInt(DisplayAreaPolicyBuilder.KEY_ROOT_DISPLAY_AREA_ID, rootDisplayAreaId);
+        return options;
+    }
+
+    private void handleShow(int rootDisplayAreaId) {
         if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
 
         mClingWindow = new ClingWindowView(mContext, mConfirm);
 
         // show the confirmation
         WindowManager.LayoutParams lp = getClingWindowLayoutParams();
-        getWindowManager().addView(mClingWindow, lp);
+        getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
     }
 
     private final Runnable mConfirm = new Runnable() {
@@ -396,7 +449,7 @@
         public void handleMessage(Message msg) {
             switch(msg.what) {
                 case SHOW:
-                    handleShow();
+                    handleShow(msg.arg1);
                     break;
                 case HIDE:
                     handleHide();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 29c7ff1..7085156 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -35,7 +35,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
-import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.app.WindowConfiguration.windowingModeToString;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -148,7 +147,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_BEFORE_ANIM;
 
@@ -597,10 +595,6 @@
     @Nullable
     private ActivityRecord mResumedActivity = null;
 
-    /** Last activity that is used to compute the Task bounds. */
-    @Nullable
-    private ActivityRecord mLastTaskBoundsComputeActivity;
-
     private boolean mForceShowForAllUsers;
 
     /** When set, will force the task to report as invisible. */
@@ -1496,11 +1490,6 @@
     }
 
     void cleanUpActivityReferences(ActivityRecord r) {
-        // mLastTaskBoundsComputeActivity is set at leaf Task
-        if (mLastTaskBoundsComputeActivity == r) {
-            mLastTaskBoundsComputeActivity = null;
-        }
-
         // mPausingActivity is set at leaf task
         if (mPausingActivity != null && mPausingActivity == r) {
             mPausingActivity = null;
@@ -2863,7 +2852,6 @@
 
     private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
             Rect previousBounds) {
-        mLastTaskBoundsComputeActivity = getTopNonFinishingActivity(false /* includeOverlays */);
 
         int windowingMode =
                 getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
@@ -2877,7 +2865,8 @@
                 getResolvedOverrideConfiguration().windowConfiguration.getBounds();
 
         if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
-            computeFullscreenBounds(outOverrideBounds, newParentConfig);
+            // Use empty bounds to indicate "fill parent".
+            outOverrideBounds.setEmpty();
             // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
             // the parent or display is smaller than the size, the content may be cropped.
             return;
@@ -2888,21 +2877,6 @@
             computeFreeformBounds(outOverrideBounds, newParentConfig);
             return;
         }
-
-        if (isSplitScreenWindowingMode(windowingMode)
-                || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
-            // This is to compute whether the task should be letterboxed to handle non-resizable app
-            // in multi window. There is no split screen only logic.
-            computeLetterboxBounds(outOverrideBounds, newParentConfig);
-        }
-    }
-
-    /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */
-    @VisibleForTesting
-    void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
-        // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
-        outBounds.setEmpty();
-        computeLetterboxBounds(outBounds, newParentConfig);
     }
 
     /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
@@ -2934,94 +2908,6 @@
         }
     }
 
-    /**
-     * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
-     * change and the requested orientation is different from the parent.
-     */
-    private void computeLetterboxBounds(@NonNull Rect outBounds,
-            @NonNull Configuration newParentConfig) {
-        if (handlesOrientationChangeFromDescendant()) {
-            // No need to letterbox at task level. Display will handle fixed-orientation requests.
-            return;
-        }
-
-        final int parentOrientation = newParentConfig.orientation;
-        // Use the top activity as the reference of orientation. Don't include overlays because
-        // it is usually not the actual content or just temporarily shown.
-        // E.g. ForcedResizableInfoActivity.
-        final ActivityRecord refActivity = getTopNonFinishingActivity(false /* includeOverlays */);
-
-        // If the task or the reference activity requires a different orientation (either by
-        // override or activityInfo), make it fit the available bounds by scaling down its bounds.
-        final int overrideOrientation = getRequestedOverrideConfiguration().orientation;
-        final int forcedOrientation =
-                (overrideOrientation != ORIENTATION_UNDEFINED || refActivity == null)
-                        ? overrideOrientation : refActivity.getRequestedConfigurationOrientation();
-        if (forcedOrientation == ORIENTATION_UNDEFINED || forcedOrientation == parentOrientation) {
-            return;
-        }
-
-        final ActivityRecord.CompatDisplayInsets compatDisplayInsets =
-                refActivity == null ? null : refActivity.getCompatDisplayInsets();
-        if (compatDisplayInsets != null && !compatDisplayInsets.mIsTaskLetterboxed) {
-            // App prefers to keep its original size.
-            // If the size compat is from previous task letterboxing, we may want to have task
-            // letterbox again, otherwise it will show the size compat restart button even if the
-            // restart bounds will be the same.
-            return;
-        }
-
-        final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
-        final int parentWidth = parentBounds.width();
-        final int parentHeight = parentBounds.height();
-        float aspect = Math.max(parentWidth, parentHeight)
-                / (float) Math.min(parentWidth, parentHeight);
-
-        // Adjust the Task letterbox bounds to fit the app request aspect ratio in order to use the
-        // extra available space.
-        if (refActivity != null) {
-            final float maxAspectRatio = refActivity.info.maxAspectRatio;
-            final float minAspectRatio = refActivity.info.minAspectRatio;
-            if (aspect > maxAspectRatio && maxAspectRatio != 0) {
-                aspect = maxAspectRatio;
-            } else if (aspect < minAspectRatio) {
-                aspect = minAspectRatio;
-            }
-        }
-
-        // Override from config_letterboxAspectRatio or via ADB with set-letterbox-aspect-ratio.
-        final float letterboxAspectRatioOverride = mWmService.getTaskLetterboxAspectRatio();
-        // Activity min/max aspect ratio restrictions will be respected by the activity-level
-        // letterboxing (size-compat mode). Therefore this override can control the maximum screen
-        // area that can be occupied by the app in the letterbox mode.
-        aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
-                ? letterboxAspectRatioOverride : aspect;
-
-        // Store the current bounds to be able to revert to size compat mode values below if needed.
-        mTmpFullBounds.set(outBounds);
-        if (forcedOrientation == ORIENTATION_LANDSCAPE) {
-            final int height = (int) Math.rint(parentWidth / aspect);
-            final int top = parentBounds.centerY() - height / 2;
-            outBounds.set(parentBounds.left, top, parentBounds.right, top + height);
-        } else {
-            final int width = (int) Math.rint(parentHeight / aspect);
-            final int left = parentBounds.centerX() - width / 2;
-            outBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
-        }
-
-        if (compatDisplayInsets != null) {
-            compatDisplayInsets.getBoundsByRotation(
-                    mTmpBounds, newParentConfig.windowConfiguration.getRotation());
-            if (outBounds.width() != mTmpBounds.width()
-                    || outBounds.height() != mTmpBounds.height()) {
-                // The app shouldn't be resized, we only do task letterboxing if the compat bounds
-                // is also from the same task letterbox. Otherwise, clear the task bounds to show
-                // app in size compat mode.
-                outBounds.set(mTmpFullBounds);
-            }
-        }
-    }
-
     Rect updateOverrideConfigurationFromLaunchBounds() {
         // If the task is controlled by another organized task, do not set override
         // configurations and let its parent (organized task) to control it;
@@ -3036,11 +2922,6 @@
         return bounds;
     }
 
-    @Nullable
-    ActivityRecord getLastTaskBoundsComputeActivity() {
-        return mLastTaskBoundsComputeActivity;
-    }
-
     /** Updates the task's bounds and override configuration to match what is expected for the
      * input root task. */
     void updateOverrideConfigurationForRootTask(Task inRootTask) {
@@ -3939,12 +3820,6 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
-    boolean isTaskLetterboxed() {
-        // No letterbox for multi window root task
-        return !matchParentBounds()
-                && (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask());
-    }
-
     @Override
     boolean fillsParent() {
         // From the perspective of policy, we still want to report that this task fills parent
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 106db0b..82d9c21 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -234,19 +234,28 @@
             }
         }
 
-        // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy
-        // that legacy (pre-D) apps and those apps that can't handle multiple screen density well
-        // are forced to be maximized. The rest of this step is to define the default policy when
-        // there is no initial bounds or a fully resolved current params from callers.
+        // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the
+        // policies related to unresizable apps here. If an app is unresizable and the freeform
+        // size-compat mode is enabled, it can be launched in freeform depending on other properties
+        // such as orientation. Otherwise, the app is forcefully launched in maximized. The rest of
+        // this step is to define the default policy when there is no initial bounds or a fully
+        // resolved current params from callers.
         if (display.inFreeformWindowingMode()) {
             if (launchMode == WINDOWING_MODE_PINNED) {
                 if (DEBUG) appendLog("picture-in-picture");
-            } else if (!mSupervisor.mService.mSizeCompatFreeform && !root.isResizeable()) {
-                // We're launching an activity in size-compat mode and they aren't allowed in
-                // freeform, so force it to be maximized.
-                launchMode = WINDOWING_MODE_FULLSCREEN;
-                outParams.mBounds.setEmpty();
-                if (DEBUG) appendLog("forced-maximize");
+            } else if (!root.isResizeable()) {
+                if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) {
+                    launchMode = WINDOWING_MODE_FREEFORM;
+                    if (outParams.mBounds.isEmpty()) {
+                        getTaskBounds(root, display, layout, launchMode, hasInitialBounds,
+                                outParams.mBounds);
+                    }
+                    if (DEBUG) appendLog("unresizable-freeform");
+                } else {
+                    launchMode = WINDOWING_MODE_FULLSCREEN;
+                    outParams.mBounds.setEmpty();
+                    if (DEBUG) appendLog("unresizable-forced-maximize");
+                }
             }
         } else {
             if (DEBUG) appendLog("non-freeform-display");
@@ -322,7 +331,6 @@
             }
             getTaskBounds(root, display, layout, resolvedMode, hasInitialBounds, outParams.mBounds);
         }
-
         return RESULT_CONTINUE;
     }
 
@@ -562,6 +570,28 @@
         outBounds.offset(xOffset, yOffset);
     }
 
+    private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
+            TaskDisplayArea displayArea) {
+        // TODO(176061101): Migrate |mSizeCompatFreeform| to |mSupportsNonResizableMultiWindow|.
+        if (!mSupervisor.mService.mSizeCompatFreeform || activity.isResizeable()) {
+            return false;
+        }
+        final DisplayContent display = displayArea.getDisplayContent();
+        if (display == null) {
+            return false;
+        }
+
+        final int displayOrientation = orientationFromBounds(displayArea.getBounds());
+        final int activityOrientation = resolveOrientation(activity, display,
+                displayArea.getBounds());
+        if (displayArea.getWindowingMode() == WINDOWING_MODE_FREEFORM
+                && displayOrientation != activityOrientation) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Resolves activity requested orientation to 4 categories:
      * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cca85b2..c0ccd81 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -253,6 +253,7 @@
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.view.RemoteAnimationAdapter;
+import android.view.ScrollCaptureResponse;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -7159,12 +7160,14 @@
         }
         final long token = Binder.clearCallingIdentity();
         try {
+            ScrollCaptureResponse.Builder responseBuilder = new ScrollCaptureResponse.Builder();
             synchronized (mGlobalLock) {
                 DisplayContent dc = mRoot.getDisplayContent(displayId);
                 if (dc == null) {
                     ProtoLog.e(WM_ERROR,
                             "Invalid displayId for requestScrollCapture: %d", displayId);
-                    callbacks.onUnavailable();
+                    responseBuilder.setDescription(String.format("bad displayId: %d", displayId));
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                     return;
                 }
                 WindowState topWindow = null;
@@ -7173,17 +7176,20 @@
                 }
                 WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
                 if (targetWindow == null) {
-                    callbacks.onUnavailable();
+                    responseBuilder.setDescription("findScrollCaptureTargetWindow returned null");
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                     return;
                 }
-                // Forward to the window for handling.
                 try {
+                    // Forward to the window for handling, which will respond using the callback.
                     targetWindow.mClient.requestScrollCapture(callbacks);
                 } catch (RemoteException e) {
                     ProtoLog.w(WM_ERROR,
                             "requestScrollCapture: caught exception dispatching to window."
                                     + "token=%s", targetWindow.mClient.asBinder());
-                    callbacks.onUnavailable();
+                    responseBuilder.setWindowTitle(targetWindow.getName());
+                    responseBuilder.setDescription(String.format("caught exception: %s", e));
+                    callbacks.onScrollCaptureResponse(responseBuilder.build());
                 }
             }
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ae5beb..1fc7041 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3827,13 +3827,21 @@
     /** @return true when the window should be letterboxed. */
     boolean isLetterboxedAppWindow() {
         // Fullscreen mode but doesn't fill display area.
-        return (!inMultiWindowMode() && !matchesDisplayAreaBounds())
-                // Activity in size compat.
-                || (mActivityRecord != null && mActivityRecord.inSizeCompatMode())
-                // Task letterboxed.
-                || (getTask() != null && getTask().isTaskLetterboxed())
-                // Letterboxed for display cutout.
-                || isLetterboxedForDisplayCutout();
+        if (!inMultiWindowMode() && !matchesDisplayAreaBounds()) {
+            return true;
+        }
+        if (mActivityRecord != null) {
+            // Activity in size compat.
+            if (mActivityRecord.inSizeCompatMode()) {
+                return true;
+            }
+            // Letterbox for fixed orientation.
+            if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) {
+                return true;
+            }
+        }
+        // Letterboxed for display cutout.
+        return isLetterboxedForDisplayCutout();
     }
 
     /** Returns {@code true} if the window is letterboxed for the display cutout. */
diff --git a/services/net/Android.bp b/services/net/Android.bp
index c68004a..b01e425 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -52,6 +52,7 @@
     libs: [
         "unsupportedappusage",
         "framework-wifi-util-lib",
+        "framework-connectivity"
     ],
     static_libs: [
         // All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
index deb3147..9447f39 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationCoreApiTest.kt
@@ -16,8 +16,9 @@
 
 package com.android.server.pm.test.verify.domain
 
-import android.content.pm.verify.domain.DomainVerificationRequest
+import android.content.pm.verify.domain.DomainSet
 import android.content.pm.verify.domain.DomainVerificationInfo
+import android.content.pm.verify.domain.DomainVerificationRequest
 import android.content.pm.verify.domain.DomainVerificationUserSelection
 import android.os.Parcel
 import android.os.Parcelable
@@ -27,6 +28,7 @@
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import java.util.UUID
+import kotlin.random.Random
 
 @RunWith(Parameterized::class)
 class DomainVerificationCoreApiTest {
@@ -40,18 +42,25 @@
             assertThat(value).containsExactlyEntriesIn(other)
         }
 
+        private val massiveSet by lazy {
+            val fragmentOf21 = ".com.example.test.app"
+            val list = mutableListOf("prefix$fragmentOf21")
+            var totalSize = 0
+            // Slightly overshoot a size of 1MB
+            while (totalSize < (1024 * 512)) {
+                val nextValue = "${list.last()}$fragmentOf21"
+                totalSize += nextValue.length
+                list += nextValue
+            }
+            list.toSet()
+        }
+
         @JvmStatic
-        @Parameterized.Parameters
+        @Parameterized.Parameters(name = "{0}")
         fun parameters() = arrayOf(
             Parameter(
-                initial = {
-                    DomainVerificationRequest(
-                        setOf(
-                            "com.test.pkg.one",
-                            "com.test.pkg.two"
-                        )
-                    )
-                },
+                testName = "DomainVerificationRequest",
+                initial = { DomainVerificationRequest(massiveSet) },
                 unparcel = { DomainVerificationRequest.CREATOR.createFromParcel(it) },
                 assertion = { first, second ->
                     assertAll<DomainVerificationRequest, Set<String>>(first, second,
@@ -61,15 +70,12 @@
                 }
             ),
             Parameter(
+                testName = "DomainVerificationInfo",
                 initial = {
                     DomainVerificationInfo(
                         UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
                         "com.test.pkg",
-                        mapOf(
-                            "example.com" to 0,
-                            "example.org" to 1,
-                            "example.new" to 1000
-                        )
+                        massiveSet.withIndex().associate { it.value to it.index }
                     )
                 },
                 unparcel = { DomainVerificationInfo.CREATOR.createFromParcel(it) },
@@ -86,17 +92,15 @@
                 }
             ),
             Parameter(
+                testName = "DomainVerificationUserSelection",
                 initial = {
                     DomainVerificationUserSelection(
                         UUID.fromString("703f6d34-6241-4cfd-8176-2e1d23355811"),
                         "com.test.pkg",
                         UserHandle.of(10),
                         true,
-                        mapOf(
-                            "example.com" to true,
-                            "example.org" to false,
-                            "example.new" to true
-                        )
+                        massiveSet.withIndex()
+                            .associate { it.value to (it.index % 3) }
                     )
                 },
                 unparcel = { DomainVerificationUserSelection.CREATOR.createFromParcel(it) },
@@ -114,21 +118,35 @@
                         first, second, { it.isLinkHandlingAllowed },
                         { it.component4() }, IS_EQUAL_TO
                     )
-                    assertAll<DomainVerificationUserSelection, Map<String, Boolean>>(
-                        first, second, { it.hostToUserSelectionMap },
+                    assertAll<DomainVerificationUserSelection, Map<String, Int>>(
+                        first, second, { it.hostToStateMap },
                         { it.component5() }, IS_MAP_EQUAL_TO
                     )
                 }
+            ),
+            Parameter(
+                testName = "DomainSet",
+                initial = { DomainSet(massiveSet) },
+                unparcel = { DomainSet.CREATOR.createFromParcel(it) },
+                assertion = { first, second ->
+                    assertAll<DomainSet, Set<String>>(
+                        first, second,
+                        { it.domains }, assertion = IS_EQUAL_TO
+                    )
+                }
             )
         )
 
         class Parameter<T : Parcelable>(
+            val testName: String,
             val initial: () -> T,
             val unparcel: (Parcel) -> T,
             private val assertion: (first: T, second: T) -> Unit
         ) {
             @Suppress("UNCHECKED_CAST")
             fun assert(first: Any, second: Any) = assertion(first as T, second as T)
+
+            override fun toString() = testName
         }
 
         private fun <T> assertAll(vararg values: T, block: (value: T, other: T) -> Unit) {
@@ -141,11 +159,17 @@
             first: T,
             second: T,
             fieldValue: (T) -> V,
-            componentValue: (T) -> V,
+            componentValue: ((T) -> V)? = null,
             assertion: (value: V, other: V) -> Unit
         ) {
-            val values = arrayOf<Any>(fieldValue(first), fieldValue(second),
-                    componentValue(first), componentValue(second))
+            val values = mutableListOf<Any>(fieldValue(first), fieldValue(second))
+                .apply {
+                    componentValue?.let {
+                        add(it(first))
+                        add(it(second))
+                    }
+                }
+                .toTypedArray()
             values.indices.drop(1).forEach {
                 @Suppress("UNCHECKED_CAST")
                 assertion(values[0] as V, values[it] as V)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 2d23fb4..89394837 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -244,6 +244,14 @@
                 service(Type.LEGACY_QUERENT, "getLegacyUserState") {
                     getLegacyState(it.targetPackageName, it.userId)
                 },
+                service(Type.OWNER_QUERENT, "getOwnersForDomain") {
+                    // Re-use package name, since the result itself isn't relevant
+                    getOwnersForDomain(it.targetPackageName)
+                },
+                service(Type.OWNER_QUERENT_USER, "getOwnersForDomainUserId") {
+                    // Re-use package name, since the result itself isn't relevant
+                    getOwnersForDomain(it.targetPackageName, it.userId)
+                },
             )
         }
 
@@ -327,6 +335,7 @@
                 domainSetId
             )
         ) {
+            whenever(getName()) { packageName }
             whenever(getPkg()) { mockPkg(packageName) }
             whenever(this.domainSetId) { domainSetId }
             whenever(userState) {
@@ -357,6 +366,8 @@
             Type.SELECTOR_USER -> approvedUserSelector(verifyCrossUser = true)
             Type.LEGACY_QUERENT -> legacyQuerent()
             Type.LEGACY_SELECTOR -> legacyUserSelector()
+            Type.OWNER_QUERENT -> ownerQuerent(verifyCrossUser = false)
+            Type.OWNER_QUERENT_USER -> ownerQuerent(verifyCrossUser = true)
         }.run { /*exhaust*/ }
     }
 
@@ -628,6 +639,80 @@
         runTestCases(callingUserId, notCallingUserId, throws = false)
     }
 
+    private fun ownerQuerent(verifyCrossUser: Boolean) {
+        val allowQueryAll = AtomicBoolean(false)
+        val allowUserSelection = AtomicBoolean(false)
+        val allowInteractAcrossUsers = AtomicBoolean(false)
+        val context: Context = mockThrowOnUnmocked {
+            initPermission(
+                allowQueryAll,
+                android.Manifest.permission.QUERY_ALL_PACKAGES
+            )
+            initPermission(
+                allowUserSelection,
+                android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION
+            )
+            initPermission(
+                allowInteractAcrossUsers,
+                android.Manifest.permission.INTERACT_ACROSS_USERS
+            )
+        }
+        val target = params.construct(context)
+
+        fun runTestCases(callingUserId: Int, targetUserId: Int, throws: Boolean) {
+            // Owner querent makes no distinction by UID
+            val allUids = INTERNAL_UIDS + VERIFIER_UID + NON_VERIFIER_UID
+            if (throws) {
+                allUids.forEach {
+                    assertFails {
+                        runMethod(target, it, visible = true, callingUserId, targetUserId)
+                    }
+                }
+            } else {
+                allUids.forEach {
+                    runMethod(target, it, visible = true, callingUserId, targetUserId)
+                }
+            }
+        }
+
+        val callingUserId = 0
+        val notCallingUserId = 1
+
+        runTestCases(callingUserId, callingUserId, throws = true)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = true)
+        }
+
+        allowQueryAll.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = true)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = true)
+        }
+
+        allowUserSelection.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = true)
+        }
+
+        allowQueryAll.set(false)
+
+        runTestCases(callingUserId, callingUserId, throws = true)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = true)
+        }
+
+        allowQueryAll.set(true)
+        allowInteractAcrossUsers.set(true)
+
+        runTestCases(callingUserId, callingUserId, throws = false)
+        if (verifyCrossUser) {
+            runTestCases(callingUserId, notCallingUserId, throws = false)
+        }
+    }
+
     private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
         whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
             if (!boolean.get()) {
@@ -694,6 +779,12 @@
         LEGACY_QUERENT,
 
         // Holding the legacy preferred apps permission
-        LEGACY_SELECTOR
+        LEGACY_SELECTOR,
+
+        // Holding user setting permission, but not targeting a package
+        OWNER_QUERENT,
+
+        // Holding user setting permission, but not targeting a package, but targeting cross user
+        OWNER_QUERENT_USER,
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt
new file mode 100644
index 0000000..48056a2
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerUserSelectionOverrideTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.verify.domain
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.parsing.component.ParsedActivity
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.content.pm.verify.domain.DomainVerificationManager
+import android.content.pm.verify.domain.DomainVerificationUserSelection
+import android.os.Build
+import android.os.PatternMatcher
+import android.os.Process
+import android.util.ArraySet
+import androidx.test.InstrumentationRegistry
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
+import java.util.UUID
+
+class DomainVerificationManagerUserSelectionOverrideTest {
+
+    companion object {
+        private const val PKG_ONE = "com.test.one"
+        private const val PKG_TWO = "com.test.two"
+        private val UUID_ONE = UUID.fromString("1b041c96-8d37-4932-a858-561bfac5947c")
+        private val UUID_TWO = UUID.fromString("a3389c16-7f9f-4e86-85e3-500d1249c74c")
+
+        private val DOMAIN_ONE =
+            DomainVerificationManagerUserSelectionOverrideTest::class.java.packageName
+
+        private const val STATE_NONE = DomainVerificationUserSelection.DOMAIN_STATE_NONE
+        private const val STATE_SELECTED = DomainVerificationUserSelection.DOMAIN_STATE_SELECTED
+        private const val STATE_VERIFIED = DomainVerificationUserSelection.DOMAIN_STATE_VERIFIED
+    }
+
+    private val pkg1 = mockPkgSetting(PKG_ONE, UUID_ONE)
+    private val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO)
+
+    fun makeManager(): DomainVerificationManager =
+        DomainVerificationService(mockThrowOnUnmocked {
+            // Assume the test has every permission necessary
+            whenever(enforcePermission(anyString(), anyInt(), anyInt(), anyString()))
+            whenever(checkPermission(anyString(), anyInt(), anyInt())) {
+                PackageManager.PERMISSION_GRANTED
+            }
+        }, mockThrowOnUnmocked {
+            whenever(linkedApps) { ArraySet<String>() }
+        }, mockThrowOnUnmocked {
+            whenever(isChangeEnabled(anyLong(), any())) { true }
+        }).apply {
+            setConnection(mockThrowOnUnmocked {
+                whenever(filterAppAccess(anyString(), anyInt(), anyInt())) { false }
+                whenever(scheduleWriteSettings())
+
+                // Need to provide an internal UID so some permission checks are ignored
+                whenever(callingUid) { Process.ROOT_UID }
+                whenever(callingUserId) { 0 }
+                whenever(getPackageSettingLocked(PKG_ONE)) { pkg1 }
+                whenever(getPackageSettingLocked(PKG_TWO)) { pkg2 }
+                whenever(getPackageLocked(PKG_ONE)) { pkg1.getPkg() }
+                whenever(getPackageLocked(PKG_TWO)) { pkg2.getPkg() }
+            })
+            addPackage(pkg1)
+            addPackage(pkg2)
+
+            // Starting state for all tests is to have domain 1 enabled for the first package
+            setDomainVerificationUserSelection(UUID_ONE, setOf(DOMAIN_ONE), true)
+
+            assertThat(stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
+        }
+
+    fun mockPkgSetting(pkgName: String, domainSetId: UUID) = mockThrowOnUnmocked<PackageSetting> {
+        val pkg = mockThrowOnUnmocked<AndroidPackage> {
+            whenever(packageName) { pkgName }
+            whenever(targetSdkVersion) { Build.VERSION_CODES.S }
+
+            val activityList = listOf(
+                ParsedActivity().apply {
+                    addIntent(
+                        ParsedIntentInfo().apply {
+                            autoVerify = true
+                            addAction(Intent.ACTION_VIEW)
+                            addCategory(Intent.CATEGORY_BROWSABLE)
+                            addCategory(Intent.CATEGORY_DEFAULT)
+                            addDataScheme("http")
+                            addDataScheme("https")
+                            addDataPath("/sub", PatternMatcher.PATTERN_LITERAL)
+                            addDataAuthority(DOMAIN_ONE, null)
+                        }
+                    )
+                    addIntent(
+                        ParsedIntentInfo().apply {
+                            autoVerify = true
+                            addAction(Intent.ACTION_VIEW)
+                            addCategory(Intent.CATEGORY_BROWSABLE)
+                            addCategory(Intent.CATEGORY_DEFAULT)
+                            addDataScheme("http")
+                            addDataPath("/sub2", PatternMatcher.PATTERN_LITERAL)
+                            addDataAuthority("example2.com", null)
+                        }
+                    )
+                },
+            )
+
+            whenever(activities) { activityList }
+        }
+
+        whenever(getPkg()) { pkg }
+        whenever(getName()) { pkgName }
+        whenever(this.domainSetId) { domainSetId }
+        whenever(getInstantApp(anyInt())) { false }
+        whenever(firstInstallTime) { 0L }
+    }
+
+    @Test
+    fun anotherPackageTakeoverSuccess() {
+        val manager = makeManager()
+
+        // Attempt override by package 2
+        manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+
+        // 1 loses approval
+        assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_NONE)
+
+        // 2 gains approval
+        assertThat(manager.stateFor(PKG_TWO, DOMAIN_ONE)).isEqualTo(STATE_SELECTED)
+
+        // 2 is the only owner
+        assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+            .containsExactly(PKG_TWO)
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun anotherPackageTakeoverFailure() {
+        val manager = makeManager()
+
+        // Verify 1 to give it a higher approval level
+        manager.setDomainVerificationStatus(UUID_ONE, setOf(DOMAIN_ONE),
+            DomainVerificationManager.STATE_SUCCESS)
+        assertThat(manager.stateFor(PKG_ONE, DOMAIN_ONE)).isEqualTo(STATE_VERIFIED)
+        assertThat(manager.getOwnersForDomain(DOMAIN_ONE).map { it.packageName })
+            .containsExactly(PKG_ONE)
+
+        // Attempt override by package 2
+        manager.setDomainVerificationUserSelection(UUID_TWO, setOf(DOMAIN_ONE), true)
+    }
+
+    private fun DomainVerificationManager.stateFor(pkgName: String, host: String) =
+        getDomainVerificationUserSelection(pkgName)!!.hostToStateMap[host]
+}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
index a76d8ce..439048c 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationModelExtensions.kt
@@ -34,7 +34,7 @@
 operator fun DomainVerificationUserSelection.component2() = packageName
 operator fun DomainVerificationUserSelection.component3() = user
 operator fun DomainVerificationUserSelection.component4() = isLinkHandlingAllowed
-operator fun DomainVerificationUserSelection.component5() = hostToUserSelectionMap
+operator fun DomainVerificationUserSelection.component5() = hostToStateMap
 
 operator fun DomainVerificationPersistence.ReadResult.component1() = active
 operator fun DomainVerificationPersistence.ReadResult.component2() = restored
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 48518f46..010eacf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -237,6 +237,7 @@
                 TEST_UUID
             )
         ) {
+            whenever(getName()) { TEST_PKG }
             whenever(getPkg()) { mockPkg() }
             whenever(domainSetId) { TEST_UUID }
             whenever(userState) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 775276b..f7f5928 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 
@@ -611,6 +612,7 @@
 
     private static NetworkCapabilities createCapabilities() {
         return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_VALIDATED);
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
index 69fe140..e0c8b09 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -500,7 +500,7 @@
     }
 
     @Override
-    protected boolean isAntennaInfoListeningSupported() {
+    protected boolean isAntennaInfoSupported() {
         return mIsAntennaInfoListeningSupported;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index c4c9ad0..1b58e92 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -353,7 +353,7 @@
     @Test
     public void testGetLastLocation_ClearOnMockRemoval() {
         MockLocationProvider mockProvider = new MockLocationProvider(PROPERTIES, PROVIDER_IDENTITY,
-                null);
+                Collections.emptySet());
         mockProvider.setAllowed(true);
         mManager.setMockProvider(mockProvider);
 
@@ -1049,7 +1049,7 @@
         private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>();
 
         TestProvider(ProviderProperties properties, CallerIdentity identity) {
-            super(DIRECT_EXECUTOR, identity, properties, null);
+            super(DIRECT_EXECUTOR, identity, properties, Collections.emptySet());
         }
 
         public void setProviderAllowed(boolean allowed) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index e8a0bb5..cf5db2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -43,6 +43,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collections;
+
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -72,7 +74,7 @@
                         .setAccuracy(ACCURACY_FINE)
                         .build(),
                 CallerIdentity.forTest(0, 1, "testpackage", "test"),
-                null);
+                Collections.emptySet());
 
         mProvider = new MockableLocationProvider(lock);
         mProvider.getController().setListener(mListener);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
index a1eadbe..2bc1268 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
@@ -23,6 +23,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Collections;
 
 public class FakeProvider extends AbstractLocationProvider {
 
@@ -40,7 +41,7 @@
     private final FakeProviderInterface mFakeInterface;
 
     public FakeProvider(FakeProviderInterface fakeInterface) {
-        super(Runnable::run, null, null, null);
+        super(Runnable::run, null, null, Collections.emptySet());
         mFakeInterface = fakeInterface;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
index 488e5cd..1870df9 100644
--- a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
@@ -30,6 +30,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -59,6 +60,7 @@
     }
 
     @Test
+    @Ignore("b/180015146")
     public void testAwaitCompletion() throws Exception {
         final CountDownLatch readyLatch = new CountDownLatch(2);
         final CountDownLatch startLatch = new CountDownLatch(1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index e46ab6b..029e9a3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -45,6 +45,13 @@
 import android.app.IUidObserver;
 import android.app.Person;
 import android.app.admin.DevicePolicyManager;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.IAppSearchBatchResultCallback;
+import android.app.appsearch.IAppSearchManager;
+import android.app.appsearch.IAppSearchResultCallback;
+import android.app.appsearch.PackageIdentifier;
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
@@ -78,6 +85,7 @@
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -150,6 +158,8 @@
                     return mMockUserManager;
                 case Context.DEVICE_POLICY_SERVICE:
                     return mMockDevicePolicyManager;
+                case Context.APP_SEARCH_SERVICE:
+                    return new AppSearchManager(getTestContext(), mMockAppSearchManager);
                 case Context.ROLE_SERVICE:
                     // RoleManager is final and cannot be mocked, so we only override the inject
                     // accessor methods in ShortcutService.
@@ -159,6 +169,11 @@
         }
 
         @Override
+        public String getOpPackageName() {
+            return getTestContext().getOpPackageName();
+        }
+
+        @Override
         public String getSystemServiceName(Class<?> serviceClass) {
             return getTestContext().getSystemServiceName(serviceClass);
         }
@@ -601,6 +616,123 @@
         }
     }
 
+    protected class MockAppSearchManager implements IAppSearchManager {
+
+        protected Map<String, List<PackageIdentifier>> mSchemasPackageAccessible =
+                new ArrayMap<>(1);
+
+        @Override
+        public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
+                List<String> schemasNotPlatformSurfaceable,
+                Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride,
+                int userId, IAppSearchResultCallback callback) throws RemoteException {
+            for (Map.Entry<String, List<Bundle>> entry :
+                    schemasPackageAccessibleBundles.entrySet()) {
+                final String key = entry.getKey();
+                final List<PackageIdentifier> packageIdentifiers;
+                if (!mSchemasPackageAccessible.containsKey(key)) {
+                    packageIdentifiers = new ArrayList<>(entry.getValue().size());
+                    mSchemasPackageAccessible.put(key, packageIdentifiers);
+                } else {
+                    packageIdentifiers = mSchemasPackageAccessible.get(key);
+                }
+                for (int i = 0; i < entry.getValue().size(); i++) {
+                    packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
+                }
+            }
+            callback.onResult(AppSearchResult.newSuccessfulResult(null));
+        }
+
+        @Override
+        public void getSchema(String packageName, String databaseName, int userId,
+                IAppSearchResultCallback callback) throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void putDocuments(String packageName, String databaseName,
+                List<Bundle> documentBundles, int userId, IAppSearchBatchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void getDocuments(String packageName, String databaseName, String namespace,
+                List<String> uris, Map<String, List<String>> typePropertyPaths, int userId,
+                IAppSearchBatchResultCallback callback) throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void query(String packageName, String databaseName, String queryExpression,
+                Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void globalQuery(String packageName, String queryExpression, Bundle searchSpecBundle,
+                int userId, IAppSearchResultCallback callback) throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void getNextPage(long nextPageToken, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void invalidateNextPageToken(long nextPageToken, int userId) throws RemoteException {
+
+        }
+
+        @Override
+        public void reportUsage(String packageName, String databaseName, String namespace,
+                String uri, long usageTimeMillis, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void removeByUri(String packageName, String databaseName, String namespace,
+                List<String> uris, int userId, IAppSearchBatchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void removeByQuery(String packageName, String databaseName, String queryExpression,
+                Bundle searchSpecBundle, int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public void persistToDisk(int userId) throws RemoteException {
+
+        }
+
+        @Override
+        public void initialize(int userId, IAppSearchResultCallback callback)
+                throws RemoteException {
+            ignore(callback);
+        }
+
+        @Override
+        public IBinder asBinder() {
+            return null;
+        }
+
+        private void ignore(IAppSearchResultCallback callback) throws RemoteException {
+            callback.onResult(AppSearchResult.newSuccessfulResult(null));
+        }
+
+        private void ignore(IAppSearchBatchResultCallback callback) throws RemoteException {
+            callback.onResult(new AppSearchBatchResult.Builder().build());
+        }
+    }
+
     public static class ShortcutActivity extends Activity {
     }
 
@@ -652,6 +784,7 @@
     protected PackageManagerInternal mMockPackageManagerInternal;
     protected UserManager mMockUserManager;
     protected DevicePolicyManager mMockDevicePolicyManager;
+    protected MockAppSearchManager mMockAppSearchManager;
     protected UserManagerInternal mMockUserManagerInternal;
     protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
     protected ActivityManagerInternal mMockActivityManagerInternal;
@@ -801,6 +934,7 @@
         mMockPackageManagerInternal = mock(PackageManagerInternal.class);
         mMockUserManager = mock(UserManager.class);
         mMockDevicePolicyManager = mock(DevicePolicyManager.class);
+        mMockAppSearchManager = new MockAppSearchManager();
         mMockUserManagerInternal = mock(UserManagerInternal.class);
         mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
         mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/pm/OWNERS b/services/tests/servicestests/src/com/android/server/pm/OWNERS
index d825dfd..e15b5f5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/pm/OWNERS
@@ -1 +1,3 @@
 include /services/core/java/com/android/server/pm/OWNERS
+
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
new file mode 100644
index 0000000..b17085e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest12.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.app.appsearch.PackageIdentifier;
+import android.content.pm.AppSearchShortcutInfo;
+
+import java.util.Random;
+
+/**
+ * Tests for {@link android.app.appsearch.AppSearchManager} and relevant APIs in ShortcutManager.
+ *
+ atest -c com.android.server.pm.ShortcutManagerTest12
+ */
+public class ShortcutManagerTest12 extends BaseShortcutManagerTest {
+
+    public void testUpdateShortcutVisibility_updatesShortcutSchema() {
+
+        final byte[] cert = new byte[20];
+        new Random().nextBytes(cert);
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            mManager.updateShortcutVisibility(CALLING_PACKAGE_2, cert, true);
+            assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.containsKey(
+                    AppSearchShortcutInfo.SCHEMA_TYPE));
+            assertTrue(mMockAppSearchManager.mSchemasPackageAccessible.get(
+                    AppSearchShortcutInfo.SCHEMA_TYPE).get(0).equals(
+                            new PackageIdentifier(CALLING_PACKAGE_2, cert)));
+        });
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
index 816bc6b..33385af 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
@@ -1 +1 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/aidl/android/media/soundtrigger_middleware/OWNERS
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 488e629..990927b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -555,9 +555,10 @@
         activity.setRequestedOrientation(
                 isScreenPortrait ? SCREEN_ORIENTATION_PORTRAIT : SCREEN_ORIENTATION_LANDSCAPE);
 
-        // Asserts it has orientation derived from bounds.
-        assertEquals(isScreenPortrait ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT,
+        // Asserts it has orientation derived requested orientation (fixed orientation letterbox).
+        assertEquals(isScreenPortrait ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE,
                 activity.getConfiguration().orientation);
+        assertTrue(activity.isLetterboxedForFixedOrientationAndAspectRatio());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index f91c9d0..e9c356d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -171,7 +171,7 @@
         final Rect activityBounds = new Rect(mFirstActivity.getBounds());
 
         // DAG is portrait (860x1200), so Task and Activity fill DAG.
-        assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
         assertThat(taskBounds).isEqualTo(dagBounds);
         assertThat(activityBounds).isEqualTo(taskBounds);
@@ -194,8 +194,8 @@
         final Rect activityConfigBounds =
                 new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds());
 
-        // DAG is landscape (1200x860), Task fills parent
-        assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+        // DAG is landscape (1200x860), no fixed orientation letterbox
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
         assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
         assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
@@ -211,7 +211,7 @@
     }
 
     @Test
-    public void testLaunchLandscapeApp_taskIsLetterboxInDisplayAreaGroup() {
+    public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() {
         mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
@@ -221,17 +221,18 @@
         final Rect taskBounds = new Rect(mFirstTask.getBounds());
         final Rect activityBounds = new Rect(mFirstActivity.getBounds());
 
-        // DAG is portrait (860x1200), so Task is letterbox (860x[860x860/1200=616])
-        assertThat(mFirstTask.isTaskLetterboxed()).isTrue();
+        // DAG is portrait (860x1200), and activity is letterboxed for fixed orientation
+        // (860x[860x860/1200=616]). Task fills DAG.
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
-        assertThat(taskBounds.width()).isEqualTo(dagBounds.width());
-        assertThat(taskBounds.height())
+        assertThat(taskBounds).isEqualTo(dagBounds);
+        assertThat(activityBounds.width()).isEqualTo(dagBounds.width());
+        assertThat(activityBounds.height())
                 .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height());
-        assertThat(activityBounds).isEqualTo(taskBounds);
     }
 
     @Test
-    public void testLaunchLandscapeApp_taskLetterboxBecomesActivityLetterboxAfterRotation() {
+    public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() {
         mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
         mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda);
@@ -245,9 +246,8 @@
         final Rect newTaskBounds = new Rect(mFirstTask.getBounds());
         final Rect newActivityBounds = new Rect(mFirstActivity.getBounds());
 
-        // DAG is landscape (1200x860), Task fills parent
-        // Task letterbox size
-        assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+        // DAG is landscape (1200x860), no fixed orientation letterbox
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isTrue();
         assertThat(newDagBounds.width()).isEqualTo(dagBounds.height());
         assertThat(newDagBounds.height()).isEqualTo(dagBounds.width());
@@ -311,7 +311,7 @@
     }
 
     @Test
-    public void testResizableFixedOrientationApp_taskLevelLetterboxing() {
+    public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() {
         mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
         mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
 
@@ -324,7 +324,7 @@
         assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
         assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
         assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
-        assertThat(mFirstTask.isTaskLetterboxed()).isFalse();
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
 
         // Launch portrait on second DAG
@@ -336,13 +336,13 @@
         assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT);
         assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
         assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
-        assertThat(mSecondTask.isTaskLetterboxed()).isFalse();
+        assertThat(mSecondActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse();
         assertThat(mSecondActivity.inSizeCompatMode()).isFalse();
 
         // First activity is letterboxed in portrait as requested.
         assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE);
         assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT);
-        assertThat(mFirstTask.isTaskLetterboxed()).isTrue();
+        assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue();
         assertThat(mFirstActivity.inSizeCompatMode()).isFalse();
 
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index e843dd7..b73c664 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -23,6 +23,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
@@ -43,7 +44,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.same;
@@ -126,6 +126,7 @@
         // Put app window into freeform and then make it a compat app.
         final Rect bounds = new Rect(100, 100, 400, 600);
         mTask.setBounds(bounds);
+
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(bounds, mActivity.getBounds());
 
@@ -194,12 +195,7 @@
                 new TestDisplayContent.Builder(mAtm, 1000, 2000)
                         .setDensityDpi(200).build();
 
-        mActivity = new ActivityBuilder(mAtm)
-                .setTask(mTask)
-                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
-                .setMaxAspectRatio(1.5f)
-                .build();
-        mActivity.mVisibleRequested = true;
+        prepareUnresizable(mActivity, 1.5f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
 
         final Rect originalBounds = new Rect(mActivity.getBounds());
         final int originalDpi = mActivity.getConfiguration().densityDpi;
@@ -566,18 +562,18 @@
                 .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
                 .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
                 .build();
-        assertTrue(activity.shouldUseSizeCompatMode());
+        assertTrue(activity.shouldCreateCompatDisplayInsets());
 
         // The non-resizable activity should not be size compat because it is on a resizable task
         // in multi-window mode.
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
-        assertFalse(activity.shouldUseSizeCompatMode());
+        assertFalse(activity.shouldCreateCompatDisplayInsets());
 
         // The non-resizable activity should not be size compat because the display support
         // changing windowing mode from fullscreen to freeform.
         mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
-        assertFalse(activity.shouldUseSizeCompatMode());
+        assertFalse(activity.shouldCreateCompatDisplayInsets());
     }
 
     @Test
@@ -597,7 +593,7 @@
                         SizeCompatTests.class.getName()))
                 .setUid(android.os.Process.myUid())
                 .build();
-        assertFalse(activity.shouldUseSizeCompatMode());
+        assertFalse(activity.shouldCreateCompatDisplayInsets());
     }
 
     @Test
@@ -653,7 +649,7 @@
     }
 
     @Test
-    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedInTaskLetterbox() {
+    public void testDisplayIgnoreOrientationRequest_fixedOrientationAppLaunchedLetterbox() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -662,7 +658,6 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
-        final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect activityBounds = new Rect(mActivity.getBounds());
 
         // Display shouldn't be rotated.
@@ -670,19 +665,19 @@
                 mActivity.mDisplayContent.getLastOrientation());
         assertTrue(displayBounds.width() > displayBounds.height());
 
-        // App should launch in task level letterboxing.
-        assertTrue(mTask.isTaskLetterboxed());
+        // App should launch in fixed orientation letterbox.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
-        assertEquals(taskBounds, activityBounds);
 
-        // Task bounds should be 700x1400 with the ratio as the display.
-        assertEquals(displayBounds.height(), taskBounds.height());
+        // Activity bounds should be 700x1400 with the ratio as the display.
+        assertEquals(displayBounds.height(), activityBounds.height());
         assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
-                taskBounds.width());
+                activityBounds.width());
     }
 
     @Test
-    public void testDisplayIgnoreOrientationRequest_taskLetterboxBecameSizeCompatAfterRotate() {
+    public void
+            testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
         mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -700,7 +695,7 @@
         assertTrue(displayBounds.width() < displayBounds.height());
 
         // App should be in size compat.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
         assertEquals(activityBounds.width(), newActivityBounds.width());
         assertEquals(activityBounds.height(), newActivityBounds.height());
@@ -719,7 +714,7 @@
         Rect activityBounds = new Rect(mActivity.getBounds());
 
         // App should launch in fullscreen.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(displayBounds, activityBounds);
 
@@ -731,7 +726,7 @@
         assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should be in size compat.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
 
         // App bounds should be 700x1400 with the ratio as the display.
@@ -741,7 +736,7 @@
     }
 
     @Test
-    public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInTaskLetterbox() {
+    public void testDisplayIgnoreOrientationRequest_newLaunchedOrientationAppInLetterbox() {
         // Set up a display in landscape and ignoring orientation request.
         setUpDisplaySizeWithApp(2800, 1400);
         final DisplayContent display = mActivity.mDisplayContent;
@@ -750,7 +745,7 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mTask.isTaskLetterboxed());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
         // Launch another portrait fixed app.
@@ -765,19 +760,19 @@
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
         verify(mTask).onDescendantOrientationChanged(same(newActivity));
-        verify(mTask).computeFullscreenBounds(any(), any());
 
         final Rect displayBounds = new Rect(display.getBounds());
         final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect newActivityBounds = new Rect(newActivity.getBounds());
 
-        // Task and app bounds should be 700x1400 with the ratio as the display.
-        assertTrue(mTask.isTaskLetterboxed());
+        // Task and display bounds should be equal while activity should be letterboxed and
+        // has 700x1400 bounds with the ratio as the display.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(newActivity.inSizeCompatMode());
-        assertEquals(taskBounds, newActivityBounds);
-        assertEquals(displayBounds.height(), taskBounds.height());
+        assertEquals(taskBounds, displayBounds);
+        assertEquals(displayBounds.height(), newActivityBounds.height());
         assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
-                taskBounds.width());
+                newActivityBounds.width());
     }
 
     @Test
@@ -790,7 +785,7 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mTask.isTaskLetterboxed());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
         // Launch another portrait fixed app with max aspect ratio as 1.3.
@@ -806,21 +801,20 @@
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
         verify(mTask).onDescendantOrientationChanged(same(newActivity));
-        verify(mTask).computeFullscreenBounds(any(), any());
 
         final Rect displayBounds = new Rect(display.getBounds());
         final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect newActivityBounds = new Rect(newActivity.getBounds());
 
-        // Task bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
-        assertTrue(mTask.isTaskLetterboxed());
-        assertEquals(displayBounds.height(), taskBounds.height());
-        assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
-                taskBounds.width());
+        // Task bounds should fill parent bounds.
+        assertEquals(displayBounds, taskBounds);
 
-        // App bounds should be fullscreen in Task bounds.
+        // Activity bounds should be (1400 / 1.3 = 1076)x1400 with the app requested ratio.
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(newActivity.inSizeCompatMode());
-        assertEquals(taskBounds, newActivityBounds);
+        assertEquals(displayBounds.height(), newActivityBounds.height());
+        assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio),
+                newActivityBounds.width());
     }
 
     @Test
@@ -834,26 +828,23 @@
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
         clearInvocations(mActivity);
 
-        assertTrue(mTask.isTaskLetterboxed());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
-        assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
 
         // Rotate display to portrait.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
-        assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
 
         // App still in size compat, and the bounds don't change.
         verify(mActivity, never()).clearSizeCompatMode();
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
-        assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
         assertEquals(activityBounds, mActivity.getBounds());
     }
 
@@ -867,22 +858,22 @@
         // Portrait fixed app.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
-        // In Task letterbox
-        assertTrue(mTask.isTaskLetterboxed());
+        // In fixed orientation letterbox
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
         // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
 
         // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
 
         // In Task letterbox
-        assertTrue(mTask.isTaskLetterboxed());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
     }
 
@@ -898,22 +889,22 @@
         // Landscape fixed app.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_LANDSCAPE);
 
-        // In Task letterbox
-        assertTrue(mTask.isTaskLetterboxed());
+        // In fixed orientation letterbox
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
 
         // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
-        assertFalse(mTask.isTaskLetterboxed());
+        assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertScaled();
 
         // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
 
-        // In Task letterbox
-        assertTrue(mTask.isTaskLetterboxed());
+        // In fixed orientation letterbox
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
         assertFalse(mActivity.inSizeCompatMode());
     }
 
@@ -972,21 +963,21 @@
         addWindowToActivity(mActivity);
         mActivity.mRootWindowContainer.performSurfacePlacement();
 
-        // Split screen is also in portrait [1000,1400], so Task should be in letterbox, and
-        // activity fills task.
-        assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation);
+        // Split screen is also in portrait [1000,1400], so activty should be in fixed orientation
+        // letterbox.
+        assertEquals(ORIENTATION_PORTRAIT, mTask.getConfiguration().orientation);
         assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
         assertFitted();
-        assertTrue(mTask.isTaskLetterboxed());
+        assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
 
-        // Letterbox should fill the gap between the split screen and the letterboxed task.
+        // Letterbox should fill the gap between the split screen and the letterboxed activity.
         final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
-        final Rect letterboxedTaskBounds = new Rect(mTask.getBounds());
-        assertTrue(primarySplitBounds.contains(letterboxedTaskBounds));
-        assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left,
-                letterboxedTaskBounds.top - primarySplitBounds.top,
-                primarySplitBounds.right - letterboxedTaskBounds.right,
-                primarySplitBounds.bottom - letterboxedTaskBounds.bottom),
+        final Rect letterboxedBounds = new Rect(mActivity.getBounds());
+        assertTrue(primarySplitBounds.contains(letterboxedBounds));
+        assertEquals(new Rect(letterboxedBounds.left - primarySplitBounds.left,
+                letterboxedBounds.top - primarySplitBounds.top,
+                primarySplitBounds.right - letterboxedBounds.right,
+                primarySplitBounds.bottom - letterboxedBounds.bottom),
                 mActivity.getLetterboxInsets());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 4984799..f8346efb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -683,12 +683,12 @@
     }
 
     @Test
-    public void testLaunchesAppInWindowOnFreeformDisplay() {
+    public void testLaunchesPortraitSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
         mAtm.mSizeCompatFreeform = true;
         final TestDisplayContent freeformDisplay = createNewDisplayContent(
                 WINDOWING_MODE_FREEFORM);
 
-        Rect expectedLaunchBounds = new Rect(0, 0, 200, 100);
+        Rect expectedLaunchBounds = new Rect(0, 0, 100, 200);
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -699,6 +699,7 @@
         mCurrent.mBounds.set(expectedLaunchBounds);
 
         mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
 
         assertEquals(RESULT_CONTINUE,
                 new CalculateRequestBuilder().setOptions(options).calculate());
@@ -710,6 +711,38 @@
     }
 
     @Test
+    public void testLaunchesLandscapeSizeCompatOnFreeformLandscapeDisplayWithFreeformSizeCompat() {
+        mAtm.mSizeCompatFreeform = true;
+        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FREEFORM);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setOptions(options).calculate());
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
+    public void testLaunchesPortraitUnresizableOnFreeformDisplayWithFreeformSizeCompat() {
+        mAtm.mSizeCompatFreeform = true;
+        final TestDisplayContent freeformDisplay = createNewDisplayContent(
+                WINDOWING_MODE_FREEFORM);
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        mCurrent.mPreferredTaskDisplayArea = freeformDisplay.getDefaultTaskDisplayArea();
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+        assertEquals(RESULT_CONTINUE,
+                new CalculateRequestBuilder().setOptions(options).calculate());
+
+        assertEquivalentWindowingMode(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode,
+                WINDOWING_MODE_FREEFORM);
+    }
+
+    @Test
     public void testSkipsForceMaximizingAppsOnNonFreeformDisplay() {
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchWindowingMode(WINDOWING_MODE_FREEFORM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 0eb8c8d..d853b93 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -266,8 +266,9 @@
         root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(root, task.getRootActivity());
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
-        assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
-        assertEquals(fullScreenBounds.height(), task.getBounds().height());
+        // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds.
+        assertThat(task.getBounds().height()).isLessThan(task.getBounds().width());
+        assertEquals(fullScreenBounds, task.getBounds());
 
         // Top activity gets used
         final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(stack)
@@ -286,8 +287,11 @@
         // Fix the display orientation to portrait which is 90 degrees for the test display.
         dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
 
-        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
-        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+        // Fixed orientation request should be resolved on activity level. Task fills display
+        // bounds.
+        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+        assertEquals(fullScreenBoundsPort, task.getBounds());
 
         // in FREEFORM, no constraint
         final Rect freeformBounds = new Rect(display.getBounds());
@@ -297,10 +301,11 @@
         task.setBounds(freeformBounds);
         assertEquals(freeformBounds, task.getBounds());
 
-        // FULLSCREEN letterboxes bounds
+        // FULLSCREEN letterboxes bounds on activity level, no constraint on task level.
         stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
-        assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+        assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width());
+        assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height());
+        assertEquals(fullScreenBoundsPort, task.getBounds());
 
         // FREEFORM restores bounds as before
         stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -327,9 +332,10 @@
 
         assertEquals(fullScreenBounds, task.getBounds());
 
-        // Setting app to fixed portrait fits within parent
+        // Setting app to fixed portrait fits within parent on activity level. Task fills parent.
         root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
-        assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
+        assertThat(root.getBounds().width()).isLessThan(root.getBounds().height());
+        assertEquals(task.getBounds(), fullScreenBounds);
 
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation());
     }
@@ -424,7 +430,8 @@
         // to the input bounds.
         final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build();
         final ActivityRecord.CompatDisplayInsets compatIntsets =
-                new ActivityRecord.CompatDisplayInsets(display, activity);
+                new ActivityRecord.CompatDisplayInsets(
+                        display, activity, /* fixedOrientationBounds= */ null);
         task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets);
 
         assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1c0f640..c3eb5c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -257,24 +257,4 @@
         task.resolveOverrideConfiguration(parentConfig);
         assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
     }
-
-    @Test
-    public void testCleanUpActivityReferences_clearLastTaskBoundsComputeActivity() {
-        final Task rootTask = createTaskStackOnDisplay(mDisplayContent);
-        final Task leafTask = createTaskInStack(rootTask, 0 /* userId */);
-        final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask);
-        final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask);
-        activity1.finishing = false;
-        leafTask.resolveOverrideConfiguration(rootTask.getConfiguration());
-
-        assertEquals(activity1, leafTask.getLastTaskBoundsComputeActivity());
-
-        leafTask.cleanUpActivityReferences(activity2);
-
-        assertNotNull(leafTask.getLastTaskBoundsComputeActivity());
-
-        leafTask.cleanUpActivityReferences(activity1);
-
-        assertNull(leafTask.getLastTaskBoundsComputeActivity());
-    }
 }
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 5cf8de8..f20ee7e 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -138,6 +138,24 @@
  *     }
  * }
  * }
+ *
+ * </pre>
+ * <p id="companionInCallService">
+ * <h3>Access to InCallService for Wearable Devices</h3>
+ * <ol>
+ * If your app is a third-party companion app and wants to access InCallService APIs, what your
+ * app could do are:
+ * <p>
+ *   <ol>
+ *     <li> Declare MANAGE_ONGOING_CALLS permission in your manifest
+ *     <li> Associate with a physical wearable device via the
+ *          {@link android.companion.CompanionDeviceManager} API as a companion app. See:
+ *          https://developer.android.com/guide/topics/connectivity/companion-device-pairing
+ *     <li> Implement this InCallService with BIND_INCALL_SERVICE permission
+ *   </ol>
+ * </ol>
+ * <p>
+ *
  * </pre>
  * <p id="incomingCallNotification">
  * <h3>Showing the Incoming Call Notification</h3>
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 472d639..17749e8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1702,22 +1702,22 @@
     }
 
     /**
-     * Returns whether the caller has {@link InCallService} access for companion apps.
-     *
-     * A companion app is an app associated with a physical wearable device via the
-     * {@link android.companion.CompanionDeviceManager} API.
+     * Returns whether the caller has {@link android.Manifest.permission#MANAGE_ONGOING_CALLS}
+     * permission. The permission can be obtained by associating with a physical wearable device
+     * via the {@link android.companion.CompanionDeviceManager} API as a companion app. If the
+     * caller app has the permission, it has {@link InCallService} access to manage ongoing calls.
      *
      * @return {@code true} if the caller has {@link InCallService} access for
      *      companion app; {@code false} otherwise.
      */
-    public boolean hasCompanionInCallServiceAccess() {
+    public boolean hasManageOngoingCallsPermission() {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
-                return service.hasCompanionInCallServiceAccess(
+                return service.hasManageOngoingCallsPermission(
                         mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling hasCompanionInCallServiceAccess().", e);
+                Log.e(TAG, "RemoteException calling hasManageOngoingCallsPermission().", e);
                 if (!isSystemProcess()) {
                     e.rethrowAsRuntimeException();
                 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 88ef1b0..eb106b5 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -179,9 +179,9 @@
     boolean isInCall(String callingPackage, String callingFeatureId);
 
     /**
-     * @see TelecomServiceImpl#hasCompanionInCallServiceAccess
+     * @see TelecomServiceImpl#hasManageOngoingCallsPermission
      */
-    boolean hasCompanionInCallServiceAccess(String callingPackage);
+    boolean hasManageOngoingCallsPermission(String callingPackage);
 
     /**
      * @see TelecomServiceImpl#isInManagedCall
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4b57fcb..b52f4919 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -33,7 +33,11 @@
 import android.service.carrier.CarrierService;
 import android.telecom.TelecomManager;
 import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.ImsSsData;
+import android.telephony.ims.SipDelegateManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.feature.RcsFeature;
 
 import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.telephony.Rlog;
@@ -3672,14 +3676,13 @@
         /** Prefix of all ImsServiceEntitlement.KEY_* constants. */
         public static final String KEY_PREFIX = "imsserviceentitlement.";
 
-
         /** The address of the entitlement configuration server. */
-        public static final String KEY_AES_URL_STRING = KEY_PREFIX + "aes_url_string";
-
+        public static final String KEY_ENTITLEMENT_SERVER_URL_STRING =
+                KEY_PREFIX + "entitlement_server_url_string";
 
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
-            defaults.putString(KEY_AES_URL_STRING, "");
+            defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");
             return defaults;
         }
     }
@@ -4007,6 +4010,43 @@
                 KEY_PREFIX + "enable_presence_publish_bool";
 
         /**
+         * Each string in this array contains a mapping between the service-id and version portion
+         * of the service-description element and the associated IMS feature tag(s) that are
+         * associated with each element (see RCC.07 Table 7).
+         * <p>
+         * Each string contains 3 parts, which define the mapping between service-description and
+         * feature tag(s) that must be present in the IMS REGISTER for the RCS service to be
+         * published as part of the RCS PUBLISH procedure:
+         * [service-id]|[version]|[desc]|[feature_tag];[feature_tag];...
+         * <ul>
+         *   <li>[service-id]: the service-id element associated with the RCS capability.</li>
+         *   <li>[version]: The version element associated with that service-id</li>
+         *   <li>[desc]: The optional desecription element associated with that service-id</li>
+         *   <li>[feature_tag];[feature_tag]: The list of all feature tags associated with this
+         *       capability that MUST ALL be present in the IMS registration for this this
+         *       capability to be published to the network.</li>
+         * </ul>
+         * <p>
+         * Features managed by the framework will be considered capable when the ImsService reports
+         * that those services are capable via the
+         * {@link MmTelFeature#notifyCapabilitiesStatusChanged(MmTelFeature.MmTelCapabilities)} or
+         * {@link RcsFeature#notifyCapabilitiesStatusChanged(RcsFeature.RcsImsCapabilities)} APIs.
+         * For RCS services not managed by the framework, the capability of these services are
+         * determined by looking at the feature tags associated with the IMS registration using the
+         * {@link ImsRegistrationAttributes} API and mapping them to the service-description map.
+         * <p>
+         * The framework contains a default value of this key, which is based off of RCC.07
+         * specification. Capabilities based of carrier extensions may be added to this list on a
+         * carrier-by-carrier basis as required in order to support additional services in the
+         * PUBLISH. If this list contains a service-id and version that overlaps with the default,
+         * it will override the framework default.
+         * @hide
+         */
+        @SystemApi
+        public static final String KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY =
+                KEY_PREFIX + "publish_service_desc_feature_tag_map_override_string_array";
+
+        /**
          * Flag indicating whether or not this carrier supports the exchange of phone numbers with
          * the carrier's RCS presence server in order to retrieve the RCS capabilities of requested
          * contacts used in the RCS User Capability Exchange (UCE) procedure. See RCC.71, section 3
@@ -4065,6 +4105,8 @@
             defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000);
             defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false);
+            defaults.putStringArray(KEY_PUBLISH_SERVICE_DESC_FEATURE_TAG_MAP_OVERRIDE_STRING_ARRAY,
+                    new String[] {});
             defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index af67ed2..fe7e5976 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -187,7 +187,7 @@
         return mIsSystemThresholdReportingRequestedWhileIdle;
     }
 
-    /*
+    /**
      * @return the live token of the request
      *
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f64f428..61e809b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -29,6 +29,7 @@
 import android.annotation.LongDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -8579,6 +8580,9 @@
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
     @SystemApi
     public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) {
         try {
@@ -8670,6 +8674,9 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
     public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason,
             @NetworkTypeBitMask long allowedNetworkTypes) {
         if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8708,6 +8715,9 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresFeature(
+            enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+            value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
     public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
             @AllowedNetworkTypesReason int reason) {
         if (!isValidAllowedNetworkTypesReason(reason)) {
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 5eb75e7..bdf628b 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -21,6 +21,7 @@
 import android.annotation.StringDef;
 import android.annotation.SystemApi;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -39,6 +40,15 @@
 public final class RcsContactPresenceTuple implements Parcelable {
 
     /**
+     * The service ID used to indicate that service discovery via presence is available.
+     * <p>
+     * See RCC.07 v5.0 specification for more information.
+     * @hide
+     */
+    public static final String SERVICE_ID_PRESENCE =
+            "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcse.dp";
+
+    /**
      * The service ID used to indicate that MMTEL service is available.
      * <p>
      * See the GSMA RCC.07 specification for more information.
@@ -329,6 +339,13 @@
         public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() {
             return Collections.unmodifiableList(mUnsupportedDuplexModeList);
         }
+
+        @Override
+        public String toString() {
+            return "servCaps{" + "a=" + mIsAudioCapable + ", v=" + mIsVideoCapable
+                    + ", supported=" + mSupportedDuplexModeList + ", unsupported="
+                    + mUnsupportedDuplexModeList + '}';
+        }
     }
 
     /**
@@ -487,4 +504,36 @@
     public @Nullable ServiceCapabilities getServiceCapabilities() {
         return mServiceCapabilities;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("{");
+        if (Build.IS_ENG) {
+            builder.append("u=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("u=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", id=");
+        builder.append(mServiceId);
+        builder.append(", v=");
+        builder.append(mServiceVersion);
+        builder.append(", s=");
+        builder.append(mStatus);
+        if (mTimestamp != null) {
+            builder.append(", timestamp=");
+            builder.append(mTimestamp);
+        }
+        if (mServiceDescription != null) {
+            builder.append(", servDesc=");
+            builder.append(mServiceDescription);
+        }
+        if (mServiceCapabilities != null) {
+            builder.append(", servCaps=");
+            builder.append(mServiceCapabilities);
+        }
+        builder.append("}");
+        return builder.toString();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index fe85502..9299fed 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -344,4 +345,39 @@
     public @NonNull Uri getContactUri() {
         return mContactUri;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("RcsContactUceCapability");
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append("(presence) {");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append("(options) {");
+        } else {
+            builder.append("(?) {");
+        }
+        if (Build.IS_ENG) {
+            builder.append("uri=");
+            builder.append(mContactUri);
+        } else {
+            builder.append("uri (isNull)=");
+            builder.append(mContactUri != null ? "XXX" : "null");
+        }
+        builder.append(", sourceType=");
+        builder.append(mSourceType);
+        builder.append(", requestResult=");
+        builder.append(mRequestResult);
+
+        if (mCapabilityMechanism == CAPABILITY_MECHANISM_PRESENCE) {
+            builder.append(", presenceTuples={");
+            builder.append(mPresenceTuples);
+            builder.append("}");
+        } else if (mCapabilityMechanism == CAPABILITY_MECHANISM_OPTIONS) {
+            builder.append(", featureTags={");
+            builder.append(mFeatureTags);
+            builder.append("}");
+        }
+
+        return builder.toString();
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index cc050be..34984e0 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -37,6 +37,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.util.Arrays;
 import java.util.HashMap;
 
 /**
@@ -368,7 +369,13 @@
     }
 
     private void onNotifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) {
-        mRcsConfigData = isCompressed ? RcsConfig.decompressGzip(config) : config;
+        // cache uncompressed config
+        config = isCompressed ? RcsConfig.decompressGzip(config) : config;
+        if (Arrays.equals(mRcsConfigData, config)) {
+            return;
+        }
+        mRcsConfigData = config;
+
         // can be null in testing
         if (mRcsCallbacks != null) {
             mRcsCallbacks.broadcastAction(c -> {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0048d53..e87f3d9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2325,6 +2325,16 @@
     void triggerRcsReconfiguration(int subId);
 
     /**
+     * Enables or disables the test mode for RCS VoLTE single registration.
+     */
+    void setRcsSingleRegistrationTestModeEnabled(boolean enabled);
+
+    /**
+     * Gets the test mode for RCS VoLTE single registration.
+     */
+    boolean getRcsSingleRegistrationTestModeEnabled();
+
+    /**
      * Overrides the config of RCS VoLTE single registration enabled for the device.
      */
     void setDeviceSingleRegistrationEnabledOverride(String enabled);
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 38a88d3..26afb79 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -153,31 +153,11 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerIsAlwaysVisible_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
-    }
+    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerIsAlwaysVisible_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
-    }
+    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 476708c..2c4c627 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -17,9 +17,8 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -85,55 +84,55 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
 
-    @FlakyTest
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleLayersShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index ac140f5..2bcdcd9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -91,63 +91,54 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeWindowBecomesInvisible() = testSpec.imeWindowBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppWindowBecomesInvisible() = testSpec.imeAppWindowBecomesInvisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         Surface.ROTATION_0)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppLayerBecomesInvisible() = testSpec.imeAppLayerBecomesInvisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
+    fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
 
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         Assume.assumeFalse(testSpec.isRotated)
@@ -171,7 +162,8 @@
         @JvmStatic
         fun getParams(): Collection<FlickerTestParameter> {
             return FlickerTestParameterFactory.getInstance()
-                .getConfigNonRotationTests(repetitions = 5)
+                .getConfigNonRotationTests(repetitions = 5,
+                    supportedRotations = listOf(Surface.ROTATION_0))
         }
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index c7a5178..008ba2f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
@@ -89,43 +89,43 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeWindowBecomesVisible() = testSpec.imeWindowBecomesVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun appWindowAlwaysVisibleOnTop() = testSpec.appWindowAlwaysVisibleOnTop(testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesVisible() = testSpec.imeLayerBecomesVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun layerAlwaysVisible() = testSpec.layerAlwaysVisible(testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerRotatesAndScales() {
         Assume.assumeFalse(testSpec.isRotated)
@@ -139,7 +139,7 @@
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation)
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerRotatesScales() {
         Assume.assumeFalse(testSpec.isRotated)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index e2a258a..18fac6a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.launch
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -148,17 +147,35 @@
     @Test
     fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
 
-    @Postsubmit
+    @Presubmit
     @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+    fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        Assume.assumeFalse(testSpec.isRotated)
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
 
     @FlakyTest
     @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
 
-    @FlakyTest(bugId = 141361128)
+    @Presubmit
+    @Test
+    fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest
+    @Test
+    fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
         testSpec.config.endRotation)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587..e1da3d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@
         final String typeName = ConnectivityManager.getNetworkTypeName(type);
         mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
         mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         mNetworkCapabilities.addTransportType(transport);
         switch (transport) {
             case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
new file mode 100644
index 0000000..9b0cfa9
--- /dev/null
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util
+
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
+import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener
+import android.provider.Settings
+import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
+import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.test.mock.MockContentResolver
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.R
+import com.android.internal.util.test.FakeSettingsProvider
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [MultinetworkPolicyTracker].
+ *
+ * Build, install and run with:
+ * atest android.net.util.MultinetworkPolicyTrackerTest
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MultinetworkPolicyTrackerTest {
+    private val resources = mock(Resources::class.java).also {
+        doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
+    }
+    private val telephonyManager = mock(TelephonyManager::class.java)
+    private val subscriptionManager = mock(SubscriptionManager::class.java).also {
+        doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt())
+    }
+    private val resolver = MockContentResolver().apply {
+        addProvider(Settings.AUTHORITY, FakeSettingsProvider()) }
+    private val context = mock(Context::class.java).also {
+        doReturn(Context.TELEPHONY_SERVICE).`when`(it)
+                .getSystemServiceName(TelephonyManager::class.java)
+        doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE)
+        doReturn(subscriptionManager).`when`(it)
+                .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+        doReturn(resolver).`when`(it).contentResolver
+        doReturn(resources).`when`(it).resources
+        doReturn(it).`when`(it).createConfigurationContext(any())
+        Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
+    }
+    private val tracker = MultinetworkPolicyTracker(context, null /* handler */)
+
+    private fun assertMultipathPreference(preference: Int) {
+        Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+                preference.toString())
+        tracker.updateMeteredMultipathPreference()
+        assertEquals(preference, tracker.meteredMultipathPreference)
+    }
+
+    @Test
+    fun testUpdateMeteredMultipathPreference() {
+        assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER)
+        assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY)
+        assertMultipathPreference(MULTIPATH_PREFERENCE_PERFORMANCE)
+    }
+
+    @Test
+    fun testUpdateAvoidBadWifi() {
+        Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
+        assertTrue(tracker.updateAvoidBadWifi())
+        assertFalse(tracker.avoidBadWifi)
+
+        doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
+        assertTrue(tracker.updateAvoidBadWifi())
+        assertTrue(tracker.avoidBadWifi)
+    }
+
+    @Test
+    fun testOnActiveDataSubscriptionIdChanged() {
+        val testSubId = 1000
+        val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */,
+                "TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */,
+                "123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */,
+                ""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */,
+                "1"/* cardString */)
+        doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId)
+
+        // Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in
+        // MultinetworkPolicyTracker should be also updated after subId changed.
+        Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
+        Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+                MULTIPATH_PREFERENCE_PERFORMANCE.toString())
+
+        val listenerCaptor = ArgumentCaptor.forClass(
+                ActiveDataSubscriptionIdChangedListener::class.java)
+        verify(telephonyManager, times(1))
+                .registerPhoneStateListener(any(), listenerCaptor.capture())
+        val listener = listenerCaptor.value
+        listener.onActiveDataSubscriptionIdChanged(testSubId)
+
+        // Check it get resource value with test sub id.
+        verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId)
+        verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 })
+
+        // Check if avoidBadWifi and meteredMultipathPreference values have been updated.
+        assertFalse(tracker.avoidBadWifi)
+        assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference)
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6de1075..1cfc3f9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2797,6 +2797,10 @@
 
         NetworkCapabilities filter = new NetworkCapabilities();
         filter.addCapability(capability);
+        // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+        // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+        // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+        filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
         handlerThread.start();
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -4144,6 +4148,7 @@
         handlerThread.start();
         NetworkCapabilities filter = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_CELLULAR)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_INTERNET);
         final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
                 mServiceContext, "testFactory", filter, mCsHandlerThread);
@@ -6048,6 +6053,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkDownstreamBandwidthKbps(10);
         final NetworkCapabilities wifiNc = new NetworkCapabilities()
                 .addTransportType(TRANSPORT_WIFI)
@@ -6056,6 +6062,7 @@
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .setLinkUpstreamBandwidthKbps(20);
         mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
         mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -7748,19 +7755,13 @@
             mWiFiNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
             callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
-            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
-            //  it.
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-                reset(mMockNetd);
-            }
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
             mCellNetworkAgent.removeCapability(testCap);
             callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
             callbackWithoutCap.assertNoCallback();
-            if (testCap == NET_CAPABILITY_TRUSTED) {
-                verify(mMockNetd).networkClearDefault();
-            }
+            verify(mMockNetd).networkClearDefault();
 
             mCm.unregisterNetworkCallback(callbackWithCap);
             mCm.unregisterNetworkCallback(callbackWithoutCap);