Merge "Fix typo for userFilter when registering for ACTION_USER_REMOVED"
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index bed3895..2c5965c 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,6 +1,5 @@
 # Bug component: 1344
 hdmoon@google.com
-hkuang@google.com
 jinpark@google.com
 klhyun@google.com
 lnilsson@google.com
diff --git a/apex/media/framework/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
index 13aabfc..af8184a 100644
--- a/apex/media/framework/java/android/media/Session2CommandGroup.java
+++ b/apex/media/framework/java/android/media/Session2CommandGroup.java
@@ -68,7 +68,7 @@
     /**
      * Used by parcelable creator.
      */
-    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    @SuppressWarnings({"WeakerAccess", "UnsafeParcelApi"}) /* synthetic access */
     Session2CommandGroup(Parcel in) {
         Parcelable[] commands = in.readParcelableArray(Session2Command.class.getClassLoader());
         if (commands != null) {
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index e48f234..7d47e25 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -79,7 +79,7 @@
 
     final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
     @GuardedBy("mLock")
-    final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+    final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<>();
     final NotificationManager mNotificationManager;
     MediaSessionManager mSessionManager;
 
@@ -150,8 +150,8 @@
         return null;
     }
 
-    List<Session2Token> getSession2TokensLocked(int userId) {
-        List<Session2Token> list = new ArrayList<>();
+    ArrayList<Session2Token> getSession2TokensLocked(int userId) {
+        ArrayList<Session2Token> list = new ArrayList<>();
         if (userId == ALL.getIdentifier()) {
             int size = mUserRecords.size();
             for (int i = 0; i < size; i++) {
@@ -237,28 +237,29 @@
     }
 
     void dispatchSession2Changed(int userId) {
-        MediaParceledListSlice<Session2Token> allSession2Tokens;
-        MediaParceledListSlice<Session2Token> userSession2Tokens;
+        ArrayList<Session2Token> allSession2Tokens;
+        ArrayList<Session2Token> userSession2Tokens;
 
         synchronized (mLock) {
-            allSession2Tokens =
-                    new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
-            userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
-        }
-        allSession2Tokens.setInlineCountLimit(1);
-        userSession2Tokens.setInlineCountLimit(1);
+            allSession2Tokens = getSession2TokensLocked(ALL.getIdentifier());
+            userSession2Tokens = getSession2TokensLocked(userId);
 
-        synchronized (mLock) {
             for (CallbackRecord record : mCallbackRecords) {
                 if (record.mUserId == ALL.getIdentifier()) {
                     try {
-                        record.mCallback.onSession2Changed(allSession2Tokens);
+                        MediaParceledListSlice<Session2Token> toSend =
+                                new MediaParceledListSlice<>(allSession2Tokens);
+                        toSend.setInlineCountLimit(0);
+                        record.mCallback.onSession2Changed(toSend);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                     }
                 } else if (record.mUserId == userId) {
                     try {
-                        record.mCallback.onSession2Changed(userSession2Tokens);
+                        MediaParceledListSlice<Session2Token> toSend =
+                                new MediaParceledListSlice<>(userSession2Tokens);
+                        toSend.setInlineCountLimit(0);
+                        record.mCallback.onSession2Changed(toSend);
                     } catch (RemoteException e) {
                         Log.w(TAG, "Failed to notify session2 tokens changed " + record);
                     }
@@ -382,7 +383,7 @@
             try {
                 // Check that they can make calls on behalf of the user and get the final user id
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
-                List<Session2Token> result;
+                ArrayList<Session2Token> result;
                 synchronized (mLock) {
                     result = getSession2TokensLocked(resolvedUserId);
                 }
diff --git a/api/Android.bp b/api/Android.bp
index 2e49a0c..70f995a 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,19 @@
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
+bootstrap_go_package {
+    name: "soong-api",
+    pkgPath: "android/soong/api",
+    deps: [
+        "blueprint",
+        "soong",
+        "soong-android",
+        "soong-genrule",
+    ],
+    srcs: ["api.go"],
+    pluginFor: ["soong_build"],
+}
+
 python_defaults {
     name: "python3_version_defaults",
     version: {
diff --git a/api/api.go b/api/api.go
new file mode 100644
index 0000000..976b140
--- /dev/null
+++ b/api/api.go
@@ -0,0 +1,224 @@
+// 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 api
+
+import (
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+// The intention behind this soong plugin is to generate a number of "merged"
+// API-related modules that would otherwise require a large amount of very
+// similar Android.bp boilerplate to define. For example, the merged current.txt
+// API definitions (created by merging the non-updatable current.txt with all
+// the module current.txts). This simplifies the addition of new android
+// modules, by reducing the number of genrules etc a new module must be added to.
+
+// The properties of the combined_apis module type.
+type CombinedApisProperties struct {
+	// Module libraries that have public APIs
+	Public []string
+	// Module libraries that have system APIs
+	System []string
+	// Module libraries that have module_library APIs
+	Module_lib []string
+	// Module libraries that have system_server APIs
+	System_server []string
+	// ART module library. The only API library not removed from the filtered api database, because
+	// 1) ART apis are available by default to all modules, while other module-to-module deps are
+	//    explicit and probably receive more scrutiny anyway
+	// 2) The number of ART/libcore APIs is large, so not linting them would create a large gap
+	// 3) It's a compromise. Ideally we wouldn't be filtering out any module APIs, and have
+	//    per-module lint databases that excludes just that module's APIs. Alas, that's more
+	//    difficult to achieve.
+	Art_module string
+}
+
+type CombinedApis struct {
+	android.ModuleBase
+
+	properties CombinedApisProperties
+}
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
+}
+
+var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+}
+
+type genruleProps struct {
+	Name       *string
+	Cmd        *string
+	Dists      []android.Dist
+	Out        []string
+	Srcs       []string
+	Tools      []string
+	Visibility []string
+}
+
+// Struct to pass parameters for the various merged [current|removed].txt file modules we create.
+type MergedTxtDefinition struct {
+	// "current.txt" or "removed.txt"
+	TxtFilename string
+	// The module for the non-updatable / non-module part of the api.
+	BaseTxt string
+	// The list of modules that are relevant for this merged txt.
+	Modules []string
+	// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
+	ModuleTag string
+	// public, system, module-lib or system-server
+	Scope string
+}
+
+func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) {
+	metalavaCmd := "$(location metalava)"
+	// Silence reflection warnings. See b/168689341
+	metalavaCmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
+	metalavaCmd += " --quiet --no-banner --format=v2 "
+
+	filename := txt.TxtFilename
+	if txt.Scope != "public" {
+		filename = txt.Scope + "-" + filename
+	}
+
+	props := genruleProps{}
+	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
+	props.Tools = []string{"metalava"}
+	props.Out = []string{txt.TxtFilename}
+	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
+	props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
+	props.Dists = []android.Dist{
+		{
+			Targets: []string{"droidcore"},
+			Dir:     proptools.StringPtr("api"),
+			Dest:    proptools.StringPtr(filename),
+		},
+		{
+			Targets: []string{"sdk"},
+			Dir:     proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
+			Dest:    proptools.StringPtr(txt.TxtFilename),
+		},
+	}
+	props.Visibility = []string{"//visibility:public"}
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) {
+	props := genruleProps{}
+	props.Name = proptools.StringPtr(ctx.ModuleName() + "-current.srcjar")
+	props.Tools = []string{"merge_zips"}
+	props.Out = []string{"current.srcjar"}
+	props.Cmd = proptools.StringPtr("$(location merge_zips) $(out) $(in)")
+	props.Srcs = createSrcs(":api-stubs-docs-non-updatable", modules, "{.public.stubs.source}")
+	props.Visibility = []string{"//visibility:private"} // Used by make module in //development, mind
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createFilteredApiVersions(ctx android.LoadHookContext, modules []string) {
+	props := genruleProps{}
+	props.Name = proptools.StringPtr("api-versions-xml-public-filtered")
+	props.Tools = []string{"api_versions_trimmer"}
+	props.Out = []string{"api-versions-public-filtered.xml"}
+	props.Cmd = proptools.StringPtr("$(location api_versions_trimmer) $(out) $(in)")
+	// Note: order matters: first parameter is the full api-versions.xml
+	// after that the stubs files in any order
+	// stubs files are all modules that export API surfaces EXCEPT ART
+	props.Srcs = createSrcs(":framework-doc-stubs{.api_versions.xml}", modules, ".stubs{.jar}")
+	props.Dists = []android.Dist{{Targets: []string{"sdk"}}}
+	ctx.CreateModule(genrule.GenRuleFactory, &props)
+}
+
+func createSrcs(base string, modules []string, tag string) []string {
+	a := make([]string, 0, len(modules)+1)
+	a = append(a, base)
+	for _, module := range modules {
+		a = append(a, ":"+module+tag)
+	}
+	return a
+}
+
+func remove(s []string, v string) []string {
+	s2 := make([]string, 0, len(s))
+	for _, sv := range s {
+		if sv != v {
+			s2 = append(s2, sv)
+		}
+	}
+	return s2
+}
+
+func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) {
+	var textFiles []MergedTxtDefinition
+	tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+	for i, f := range []string{"current.txt", "removed.txt"} {
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-" + f,
+			Modules:     props.Public,
+			ModuleTag:   "{.public" + tagSuffix[i],
+			Scope:       "public",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-system-" + f,
+			Modules:     props.System,
+			ModuleTag:   "{.system" + tagSuffix[i],
+			Scope:       "system",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-module-lib-" + f,
+			Modules:     props.Module_lib,
+			ModuleTag:   "{.module-lib" + tagSuffix[i],
+			Scope:       "module-lib",
+		})
+		textFiles = append(textFiles, MergedTxtDefinition{
+			TxtFilename: f,
+			BaseTxt:     ":non-updatable-system-server-" + f,
+			Modules:     props.System_server,
+			ModuleTag:   "{.system-server" + tagSuffix[i],
+			Scope:       "system-server",
+		})
+	}
+	for _, txt := range textFiles {
+		createMergedTxt(ctx, txt)
+	}
+}
+
+func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
+	createMergedTxts(ctx, a.properties)
+
+	createMergedStubsSrcjar(ctx, a.properties.Public)
+
+	// For the filtered api versions, we prune all APIs except art module's APIs.
+	createFilteredApiVersions(ctx, remove(a.properties.Public, a.properties.Art_module))
+}
+
+func combinedApisModuleFactory() android.Module {
+	module := &CombinedApis{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
+	return module
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index 8b113e1..ab51690 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1312,6 +1312,7 @@
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int shouldUseDefaultUnfoldTransition = 16844364; // 0x101064c
     field public static final int showAsAction = 16843481; // 0x10102d9
+    field public static final int showClockAndComplications;
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
@@ -27361,11 +27362,13 @@
     method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress);
     method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String);
     method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int);
+    method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix);
     method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int);
     method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String);
     method @NonNull public android.net.VpnService.Builder allowBypass();
     method @NonNull public android.net.VpnService.Builder allowFamily(int);
     method @Nullable public android.os.ParcelFileDescriptor establish();
+    method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix);
     method @NonNull public android.net.VpnService.Builder setBlocking(boolean);
     method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent);
     method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
@@ -27683,6 +27686,23 @@
 
 package android.net.vcn {
 
+  public final class VcnCellUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+    method @NonNull public java.util.Set<java.lang.String> getOperatorPlmnIds();
+    method public int getOpportunistic();
+    method public int getRoaming();
+    method @NonNull public java.util.Set<java.lang.Integer> getSimSpecificCarrierIds();
+  }
+
+  public static final class VcnCellUnderlyingNetworkTemplate.Builder {
+    ctor public VcnCellUnderlyingNetworkTemplate.Builder();
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate build();
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOperatorPlmnIds(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setOpportunistic(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setRoaming(int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setSimSpecificCarrierIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
   public final class VcnConfig implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.Set<android.net.vcn.VcnGatewayConnectionConfig> getGatewayConnectionConfigs();
@@ -27701,6 +27721,7 @@
     method @NonNull public String getGatewayConnectionName();
     method @IntRange(from=0x500) public int getMaxMtu();
     method @NonNull public long[] getRetryIntervalsMillis();
+    method @NonNull public java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities();
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
@@ -27710,6 +27731,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setVcnUnderlyingNetworkPriorities(@NonNull java.util.List<android.net.vcn.VcnUnderlyingNetworkTemplate>);
   }
 
   public class VcnManager {
@@ -27733,6 +27755,24 @@
     method public abstract void onStatusChanged(int);
   }
 
+  public abstract class VcnUnderlyingNetworkTemplate {
+    method public int getMetered();
+    field public static final int MATCH_ANY = 0; // 0x0
+    field public static final int MATCH_FORBIDDEN = 2; // 0x2
+    field public static final int MATCH_REQUIRED = 1; // 0x1
+  }
+
+  public final class VcnWifiUnderlyingNetworkTemplate extends android.net.vcn.VcnUnderlyingNetworkTemplate {
+    method @NonNull public java.util.Set<java.lang.String> getSsids();
+  }
+
+  public static final class VcnWifiUnderlyingNetworkTemplate.Builder {
+    ctor public VcnWifiUnderlyingNetworkTemplate.Builder();
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate build();
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMetered(int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
+  }
+
 }
 
 package android.nfc {
@@ -40265,6 +40305,7 @@
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
     field public static final int PROPERTY_RTT = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
+    field public static final int PROPERTY_TETHERED_CALL = 32768; // 0x8000
     field public static final int PROPERTY_VOIP_AUDIO_MODE = 4096; // 0x1000
     field public static final int PROPERTY_WIFI = 8; // 0x8
   }
@@ -40293,6 +40334,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallAudioState> CREATOR;
     field public static final int ROUTE_BLUETOOTH = 2; // 0x2
     field public static final int ROUTE_EARPIECE = 1; // 0x1
+    field public static final int ROUTE_EXTERNAL = 16; // 0x10
     field public static final int ROUTE_SPEAKER = 8; // 0x8
     field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
@@ -40567,6 +40609,7 @@
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
     field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
+    field public static final int PROPERTY_TETHERED_CALL = 16384; // 0x4000
     field public static final int PROPERTY_WIFI = 8; // 0x8
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
@@ -52181,6 +52224,7 @@
     field public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; // 0x4
     field public static final int TYPE_APPLICATION = 1; // 0x1
     field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_MAGNIFICATION_OVERLAY = 6; // 0x6
     field public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5; // 0x5
     field public static final int TYPE_SYSTEM = 3; // 0x3
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1fd03a2..ac56488 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -165,6 +165,7 @@
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER";
+    field public static final String MANAGE_SAFETY_CENTER = "android.permission.MANAGE_SAFETY_CENTER";
     field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI";
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
     field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE";
@@ -403,6 +404,7 @@
     field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
     field public static final int config_systemAppProtectionService;
     field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
+    field public static final int config_systemAutomotiveCalendarSyncManager;
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemCompanionDeviceProvider = 17039417; // 0x1040039
@@ -1045,6 +1047,7 @@
     field @Deprecated public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_LABEL";
     field public static final String EXTRA_PROVISIONING_ORGANIZATION_NAME = "android.app.extra.PROVISIONING_ORGANIZATION_NAME";
     field public static final String EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE = "android.app.extra.PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE";
+    field public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT = "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
     field public static final String EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER = "android.app.extra.PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER";
     field public static final String EXTRA_PROVISIONING_SUPPORTED_MODES = "android.app.extra.PROVISIONING_SUPPORTED_MODES";
     field public static final String EXTRA_PROVISIONING_SUPPORT_URL = "android.app.extra.PROVISIONING_SUPPORT_URL";
@@ -2428,6 +2431,25 @@
 
 package android.bluetooth.le {
 
+  public final class AdvertiseSettings implements android.os.Parcelable {
+    method public int getOwnAddressType();
+  }
+
+  public static final class AdvertiseSettings.Builder {
+    method @NonNull public android.bluetooth.le.AdvertiseSettings.Builder setOwnAddressType(int);
+  }
+
+  public final class AdvertisingSetParameters implements android.os.Parcelable {
+    method public int getOwnAddressType();
+    field public static final int ADDRESS_TYPE_DEFAULT = -1; // 0xffffffff
+    field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0
+    field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1
+  }
+
+  public static final class AdvertisingSetParameters.Builder {
+    method @NonNull public android.bluetooth.le.AdvertisingSetParameters.Builder setOwnAddressType(int);
+  }
+
   public final class BluetoothLeScanner {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.UPDATE_DEVICE_STATS}) public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
@@ -6713,8 +6735,8 @@
     method @Nullable public String acquireSharedFilterToken();
     method public void close();
     method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
-    method public int delayCallbackUntilBufferFilled(int);
-    method public int delayCallbackUntilTimeMillis(long);
+    method public int delayCallbackUntilBytesAccumulated(int);
+    method public int delayCallbackUntilMillisElapsed(long);
     method public int flush();
     method public void freeSharedFilterToken(@NonNull String);
     method @Deprecated public int getId();
@@ -6933,12 +6955,14 @@
   }
 
   public abstract class SectionSettings extends android.media.tv.tuner.filter.Settings {
+    method public int getBitWidthOfLengthField();
     method public boolean isCrcEnabled();
     method public boolean isRaw();
     method public boolean isRepeat();
   }
 
   public abstract static class SectionSettings.Builder<T extends android.media.tv.tuner.filter.SectionSettings.Builder<T>> {
+    method @NonNull public T setBitWidthOfLengthField(@IntRange(from=0) int);
     method @NonNull public T setCrcEnabled(boolean);
     method @NonNull public T setRaw(boolean);
     method @NonNull public T setRepeat(boolean);
@@ -10795,7 +10819,7 @@
 
   public class GameService extends android.app.Service {
     ctor public GameService();
-    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
     field public static final String ACTION_GAME_SERVICE = "android.service.games.action.GAME_SERVICE";
@@ -10810,7 +10834,7 @@
 
   public abstract class GameSessionService extends android.app.Service {
     ctor public GameSessionService();
-    method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+    method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method @NonNull public abstract android.service.games.GameSession onNewSession(@NonNull android.service.games.CreateGameSessionRequest);
     field public static final String ACTION_GAME_SESSION_SERVICE = "android.service.games.action.GAME_SESSION_SERVICE";
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6586ae0..66250fce 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -36,6 +36,7 @@
     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 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
     field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
     field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -66,6 +67,7 @@
   public static final class R.string {
     field public static final int config_defaultAssistant = 17039393; // 0x1040021
     field public static final int config_defaultDialer = 17039395; // 0x1040023
+    field public static final int config_systemAutomotiveCalendarSyncManager;
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
     field public static final int config_systemGallery = 17039399; // 0x1040027
@@ -1829,6 +1831,14 @@
     method @NonNull public android.os.VibrationEffect build(int);
   }
 
+  public abstract class Vibrator {
+    method public int getDefaultVibrationIntensity(int);
+    field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3
+    field public static final int VIBRATION_INTENSITY_LOW = 1; // 0x1
+    field public static final int VIBRATION_INTENSITY_MEDIUM = 2; // 0x2
+    field public static final int VIBRATION_INTENSITY_OFF = 0; // 0x0
+  }
+
   public class VintfObject {
     method public static String[] getHalNamesAndVersions();
     method @NonNull public static String getPlatformSepolicyVersion();
@@ -2036,6 +2046,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
     method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
+    method public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
   }
 
 }
@@ -2309,6 +2320,7 @@
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onStartDream(@NonNull android.view.WindowManager.LayoutParams);
     method public final void requestExit();
+    method public final boolean shouldShowComplications();
   }
 
 }
diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java
index a821dad..857c541 100644
--- a/core/java/android/accessibilityservice/GestureDescription.java
+++ b/core/java/android/accessibilityservice/GestureDescription.java
@@ -525,7 +525,7 @@
         public GestureStep(Parcel parcel) {
             timeSinceGestureStart = parcel.readLong();
             Parcelable[] parcelables =
-                    parcel.readParcelableArray(TouchPoint.class.getClassLoader());
+                    parcel.readParcelableArray(TouchPoint.class.getClassLoader(), TouchPoint.class);
             numTouchPoints = (parcelables == null) ? 0 : parcelables.length;
             touchPoints = new TouchPoint[numTouchPoints];
             for (int i = 0; i < numTouchPoints; i++) {
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b052bc5..a9ec11e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -178,17 +178,16 @@
     Point getAppTaskThumbnailSize();
     /**
      * Only callable from the system. This token grants a temporary permission to call
-     * #startActivityAsCallerWithToken. The token will time out after
-     * START_AS_CALLER_TOKEN_TIMEOUT if it is not used.
+     * #startActivityAsCaller. The token will time out after START_AS_CALLER_TOKEN_TIMEOUT
+     * if it is not used.
      *
-     * @param delegatorToken The Binder token referencing the system Activity that wants to delegate
-     *        the #startActivityAsCaller to another app. The "caller" will be the caller of this
-     *        activity's token, not the delegate's caller (which is probably the delegator itself).
+     * @param componentName The component name of the delegated component that is allowed to
+     *                      call #startActivityAsCaller with the returned token.
      *
      * @return Returns a token that can be given to a "delegate" app that may call
      *         #startActivityAsCaller
      */
-    IBinder requestStartActivityPermissionToken(in IBinder delegatorToken);
+    IBinder requestStartActivityPermissionToken(in ComponentName componentName);
 
     oneway void releaseSomeActivities(in IApplicationThread app);
     Bitmap getTaskDescriptionIcon(in String filename, int userId);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7969cda..73ebdf6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2885,6 +2885,38 @@
     public static final int RESULT_UPDATE_DEVICE_MANAGEMENT_ROLE_HOLDER_UNRECOVERABLE_ERROR = 2;
 
     /**
+     * An {@link Intent} extra which resolves to a custom user consent screen.
+     *
+     * <p>If this extra is provided to the device management role holder via either {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} or {@link
+     * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE}, the device management role holder must
+     * launch this intent which shows the custom user consent screen, replacing its own standard
+     * consent screen.
+     *
+     * <p>If this extra is provided, it is the responsibility of the intent handler to show the
+     * list of disclaimers which are normally shown by the standard consent screen:
+     * <ul>
+     *     <li>Disclaimers set by the IT admin via the {@link #EXTRA_PROVISIONING_DISCLAIMERS}
+     *     provisioning extra</li>
+     *     <li>For fully-managed device provisioning, disclaimers defined in system apps via the
+     *     {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER} and {@link
+     *     #EXTRA_PROVISIONING_DISCLAIMER_CONTENT} metadata in their manifests</li>
+     *     <li>General disclaimer relevant to the provisioning mode</li>
+     * </ul>
+     *
+     * <p>If the custom consent screens are granted by the user {@link Activity#RESULT_OK} is
+     * returned, otherwise {@link Activity#RESULT_CANCELED} is returned. The device management
+     * role holder should ensure that the provisioning flow terminates immediately if consent
+     * is not granted by the user.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String EXTRA_PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT =
+            "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
+
+    /**
      * Maximum supported password length. Kind-of arbitrary.
      * @hide
      */
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 65b2775..177de83 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -156,7 +156,7 @@
 
                 @Override
                 public void onTrustError(CharSequence message) {
-                    Message m = mHandler.obtainMessage(MSG_TRUST_ERROR);
+                    Message m = mHandler.obtainMessage(MSG_TRUST_ERROR, trustListener);
                     m.getData().putCharSequence(DATA_MESSAGE, message);
                     m.sendToTarget();
                 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 856a8e1..7e5e96d 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3064,6 +3064,9 @@
             BluetoothCsipSetCoordinator csipSetCoordinator =
                     new BluetoothCsipSetCoordinator(context, listener, this);
             return true;
+        } else if (profile == BluetoothProfile.LE_CALL_CONTROL) {
+            BluetoothLeCallControl tbs = new BluetoothLeCallControl(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -3166,6 +3169,10 @@
                         (BluetoothCsipSetCoordinator) proxy;
                 csipSetCoordinator.close();
                 break;
+            case BluetoothProfile.LE_CALL_CONTROL:
+                BluetoothLeCallControl tbs = (BluetoothLeCallControl) proxy;
+                tbs.close();
+                break;
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothLeCall.java b/core/java/android/bluetooth/BluetoothLeCall.java
new file mode 100644
index 0000000..fb7789d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCall.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2021 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * Representation of Call
+ *
+ * @hide
+ */
+public final class BluetoothLeCall implements Parcelable {
+
+    /** @hide */
+    @IntDef(prefix = "STATE_", value = {
+            STATE_INCOMING,
+            STATE_DIALING,
+            STATE_ALERTING,
+            STATE_ACTIVE,
+            STATE_LOCALLY_HELD,
+            STATE_REMOTELY_HELD,
+            STATE_LOCALLY_AND_REMOTELY_HELD
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {
+    }
+
+    /**
+     * A remote party is calling (incoming call).
+     *
+     * @hide
+     */
+    public static final int STATE_INCOMING = 0x00;
+
+    /**
+     * The process to call the remote party has started but the remote party is not
+     * being alerted (outgoing call).
+     *
+     * @hide
+     */
+    public static final int STATE_DIALING = 0x01;
+
+    /**
+     * A remote party is being alerted (outgoing call).
+     *
+     * @hide
+     */
+    public static final int STATE_ALERTING = 0x02;
+
+    /**
+     * The call is in an active conversation.
+     *
+     * @hide
+     */
+    public static final int STATE_ACTIVE = 0x03;
+
+    /**
+     * The call is connected but held locally. “Locally Held” implies that either
+     * the server or the client can affect the state.
+     *
+     * @hide
+     */
+    public static final int STATE_LOCALLY_HELD = 0x04;
+
+    /**
+     * The call is connected but held remotely. “Remotely Held” means that the state
+     * is controlled by the remote party of a call.
+     *
+     * @hide
+     */
+    public static final int STATE_REMOTELY_HELD = 0x05;
+
+    /**
+     * The call is connected but held both locally and remotely.
+     *
+     * @hide
+     */
+    public static final int STATE_LOCALLY_AND_REMOTELY_HELD = 0x06;
+
+    /**
+     * Whether the call direction is outgoing.
+     *
+     * @hide
+     */
+    public static final int FLAG_OUTGOING_CALL = 0x00000001;
+
+    /**
+     * Whether the call URI and Friendly Name are withheld by server.
+     *
+     * @hide
+     */
+    public static final int FLAG_WITHHELD_BY_SERVER = 0x00000002;
+
+    /**
+     * Whether the call URI and Friendly Name are withheld by network.
+     *
+     * @hide
+     */
+    public static final int FLAG_WITHHELD_BY_NETWORK = 0x00000004;
+
+    /** Unique UUID that identifies this call */
+    private UUID mUuid;
+
+    /** Remote Caller URI */
+    private String mUri;
+
+    /** Caller friendly name */
+    private String mFriendlyName;
+
+    /** Call state */
+    private @State int mState;
+
+    /** Call flags */
+    private int mCallFlags;
+
+    /** @hide */
+    public BluetoothLeCall(@NonNull BluetoothLeCall that) {
+        mUuid = new UUID(that.getUuid().getMostSignificantBits(),
+                that.getUuid().getLeastSignificantBits());
+        mUri = that.mUri;
+        mFriendlyName = that.mFriendlyName;
+        mState = that.mState;
+        mCallFlags = that.mCallFlags;
+    }
+
+    /** @hide */
+    public BluetoothLeCall(@NonNull UUID uuid, @NonNull String uri, @NonNull String friendlyName,
+            @State int state, int callFlags) {
+        mUuid = uuid;
+        mUri = uri;
+        mFriendlyName = friendlyName;
+        mState = state;
+        mCallFlags = callFlags;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        BluetoothLeCall that = (BluetoothLeCall) o;
+        return mUuid.equals(that.mUuid) && mUri.equals(that.mUri)
+                && mFriendlyName.equals(that.mFriendlyName) && mState == that.mState
+                && mCallFlags == that.mCallFlags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUuid, mUri, mFriendlyName, mState, mCallFlags);
+    }
+
+    /**
+     * Returns a string representation of this BluetoothLeCall.
+     *
+     * <p>
+     * Currently this is the UUID.
+     *
+     * @return string representation of this BluetoothLeCall
+     */
+    @Override
+    public String toString() {
+        return mUuid.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeParcelable(new ParcelUuid(mUuid), 0);
+        out.writeString(mUri);
+        out.writeString(mFriendlyName);
+        out.writeInt(mState);
+        out.writeInt(mCallFlags);
+    }
+
+    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeCall> CREATOR =
+    						    new Parcelable.Creator<BluetoothLeCall>() {
+        public BluetoothLeCall createFromParcel(Parcel in) {
+            return new BluetoothLeCall(in);
+        }
+
+        public BluetoothLeCall[] newArray(int size) {
+            return new BluetoothLeCall[size];
+        }
+    };
+
+    private BluetoothLeCall(Parcel in) {
+        mUuid = ((ParcelUuid) in.readParcelable(null)).getUuid();
+        mUri = in.readString();
+        mFriendlyName = in.readString();
+        mState = in.readInt();
+        mCallFlags = in.readInt();
+    }
+
+    /**
+     * Returns an UUID of this BluetoothLeCall.
+     *
+     * <p>
+     * An UUID is unique identifier of a BluetoothLeCall.
+     *
+     * @return UUID of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull UUID getUuid() {
+        return mUuid;
+    }
+
+    /**
+     * Returns a URI of the remote party of this BluetoothLeCall.
+     *
+     * @return string representation of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull String getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns a friendly name of the call.
+     *
+     * @return friendly name representation of this BluetoothLeCall
+     * @hide
+     */
+    public @NonNull String getFriendlyName() {
+        return mFriendlyName;
+    }
+
+    /**
+     * Returns the call state.
+     *
+     * @return the state of this BluetoothLeCall
+     * @hide
+     */
+    public @State int getState() {
+        return mState;
+    }
+
+    /**
+     * Returns the call flags.
+     *
+     * @return call flags
+     * @hide
+     */
+    public int getCallFlags() {
+        return mCallFlags;
+    }
+
+    /**
+     * Whether the call direction is incoming.
+     *
+     * @return true if incoming call, false otherwise
+     * @hide
+     */
+    public boolean isIncomingCall() {
+        return (mCallFlags & FLAG_OUTGOING_CALL) == 0;
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothLeCallControl.java b/core/java/android/bluetooth/BluetoothLeCallControl.java
new file mode 100644
index 0000000..fb080c9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeCallControl.java
@@ -0,0 +1,899 @@
+/*
+ * Copyright 2019 HIMSA II K/S - www.himsa.com.
+ * Represented by EHIMA - www.ehima.com
+ *
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+import android.annotation.SuppressLint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.Executor;
+
+/**
+ * This class provides the APIs to control the Call Control profile.
+ *
+ * <p>
+ * This class provides Bluetooth Telephone Bearer Service functionality,
+ * allowing applications to expose a GATT Service based interface to control the
+ * state of the calls by remote devices such as LE audio devices.
+ *
+ * <p>
+ * BluetoothLeCallControl is a proxy object for controlling the Bluetooth Telephone Bearer
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the
+ * BluetoothLeCallControl proxy object.
+ *
+ * @hide
+ */
+public final class BluetoothLeCallControl implements BluetoothProfile {
+    private static final String TAG = "BluetoothLeCallControl";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    /** @hide */
+    @IntDef(prefix = "RESULT_", value = {
+            RESULT_SUCCESS,
+            RESULT_ERROR_UNKNOWN_CALL_ID,
+            RESULT_ERROR_INVALID_URI,
+            RESULT_ERROR_APPLICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Result {
+    }
+
+    /**
+     * Opcode write was successful.
+     *
+     * @hide
+     */
+    public static final int RESULT_SUCCESS = 0;
+
+    /**
+     * Unknown call Id has been used in the operation.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_UNKNOWN_CALL_ID = 1;
+
+    /**
+     * The URI provided in {@link Callback#onPlaceCallRequest} is invalid.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_INVALID_URI = 2;
+
+    /**
+     * Application internal error.
+     *
+     * @hide
+     */
+    public static final int RESULT_ERROR_APPLICATION = 3;
+
+    /** @hide */
+    @IntDef(prefix = "TERMINATION_REASON_", value = {
+            TERMINATION_REASON_INVALID_URI,
+            TERMINATION_REASON_FAIL,
+            TERMINATION_REASON_REMOTE_HANGUP,
+            TERMINATION_REASON_SERVER_HANGUP,
+            TERMINATION_REASON_LINE_BUSY,
+            TERMINATION_REASON_NETWORK_CONGESTION,
+            TERMINATION_REASON_CLIENT_HANGUP,
+            TERMINATION_REASON_NO_SERVICE,
+            TERMINATION_REASON_NO_ANSWER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TerminationReason {
+    }
+
+    /**
+     * Remote Caller ID value used to place a call was formed improperly.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_INVALID_URI = 0x00;
+
+    /**
+     * Call fail.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_FAIL = 0x01;
+
+    /**
+     * Remote party ended call.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_REMOTE_HANGUP = 0x02;
+
+    /**
+     * Call ended from the server.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_SERVER_HANGUP = 0x03;
+
+    /**
+     * Line busy.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_LINE_BUSY = 0x04;
+
+    /**
+     * Network congestion.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NETWORK_CONGESTION = 0x05;
+
+    /**
+     * Client terminated.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_CLIENT_HANGUP = 0x06;
+
+    /**
+     * No service.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NO_SERVICE = 0x07;
+
+    /**
+     * No answer.
+     *
+     * @hide
+     */
+    public static final int TERMINATION_REASON_NO_ANSWER = 0x08;
+
+    /*
+     * Flag indicating support for hold/unhold call feature.
+     *
+     * @hide
+     */
+    public static final int CAPABILITY_HOLD_CALL = 0x00000001;
+
+    /**
+     * Flag indicating support for joining calls feature.
+     *
+     * @hide
+     */
+    public static final int CAPABILITY_JOIN_CALLS = 0x00000002;
+
+    private static final int MESSAGE_TBS_SERVICE_CONNECTED = 102;
+    private static final int MESSAGE_TBS_SERVICE_DISCONNECTED = 103;
+
+    private static final int REG_TIMEOUT = 10000;
+
+    /**
+     * The template class is used to call callback functions on events from the TBS
+     * server. Callback functions are wrapped in this class and registered to the
+     * Android system during app registration.
+     *
+     * @hide
+     */
+    public abstract static class Callback {
+
+        private static final String TAG = "BluetoothLeCallControl.Callback";
+
+        /**
+         * Called when a remote client requested to accept the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to be accepted
+         * @hide
+         */
+        public abstract void onAcceptCall(int requestId, @NonNull UUID callId);
+
+        /**
+         * A remote client has requested to terminate the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to terminate
+         * @hide
+         */
+        public abstract void onTerminateCall(int requestId, @NonNull UUID callId);
+
+        /**
+         * A remote client has requested to hold the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to be put on hold
+         * @hide
+         */
+        public void onHoldCall(int requestId, @NonNull UUID callId) {
+            Log.e(TAG, "onHoldCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+        }
+
+        /**
+         * A remote client has requested to unhold the call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The call Id requested to unhold
+         * @hide
+         */
+        public void onUnholdCall(int requestId, @NonNull UUID callId) {
+            Log.e(TAG, "onUnholdCall: unimplemented, however CAPABILITY_HOLD_CALL is set!");
+        }
+
+        /**
+         * A remote client has requested to place a call.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callId    The Id to be assigned for the new call
+         * @param uri       The caller URI requested
+         * @hide
+         */
+        public abstract void onPlaceCall(int requestId, @NonNull UUID callId, @NonNull String uri);
+
+        /**
+         * A remote client has requested to join the calls.
+         *
+         * <p>
+         * An application must call {@link BluetoothLeCallControl#requestResult} to complete the
+         * request.
+         *
+         * @param requestId The Id of the request
+         * @param callIds   The call Id list requested to join
+         * @hide
+         */
+        public void onJoinCalls(int requestId, @NonNull List<UUID> callIds) {
+            Log.e(TAG, "onJoinCalls: unimplemented, however CAPABILITY_JOIN_CALLS is set!");
+        }
+    }
+
+    private class CallbackWrapper extends IBluetoothLeCallControlCallback.Stub {
+
+        private final Executor mExecutor;
+        private final Callback mCallback;
+
+        CallbackWrapper(Executor executor, Callback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+
+        @Override
+        public void onBearerRegistered(int ccid) {
+            if (mCallback != null) {
+                mCcid = ccid;
+            } else {
+                // registration timeout
+                Log.e(TAG, "onBearerRegistered: mCallback is null");
+            }
+        }
+
+        @Override
+        public void onAcceptCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onAcceptCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onTerminateCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onTerminateCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onHoldCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onHoldCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onUnholdCall(int requestId, ParcelUuid uuid) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onUnholdCall(requestId, uuid.getUuid()));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onPlaceCall(int requestId, ParcelUuid uuid, String uri) {
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onPlaceCall(requestId, uuid.getUuid(), uri));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+
+        @Override
+        public void onJoinCalls(int requestId, List<ParcelUuid> parcelUuids) {
+            List<UUID> uuids = new ArrayList<>();
+            for (ParcelUuid parcelUuid : parcelUuids) {
+                uuids.add(parcelUuid.getUuid());
+            }
+
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onJoinCalls(requestId, uuids));
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+    };
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private volatile IBluetoothLeCallControl mService;
+    private BluetoothAdapter mAdapter;
+    private int mCcid = 0;
+    private String mToken;
+    private Callback mCallback = null;
+
+    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+        new IBluetoothStateChangeCallback.Stub() {
+        public void onBluetoothStateChange(boolean up) {
+            if (DBG)
+                Log.d(TAG, "onBluetoothStateChange: up=" + up);
+            if (!up) {
+                doUnbind();
+            } else {
+                doBind();
+            }
+        }
+    };
+
+    /**
+     * Create a BluetoothLeCallControl proxy object for interacting with the local Bluetooth
+     * telephone bearer service.
+     */
+    /* package */ BluetoothLeCallControl(Context context, ServiceListener listener) {
+        mContext = context;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mServiceListener = listener;
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        doBind();
+    }
+
+    private boolean doBind() {
+        synchronized (mConnection) {
+            if (mService == null) {
+                if (VDBG)
+                    Log.d(TAG, "Binding service...");
+                try {
+                    return mAdapter.getBluetoothManager().
+                            bindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+                            mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to bind TelephoneBearerService", e);
+                }
+            }
+        }
+        return false;
+    }
+
+    private void doUnbind() {
+        synchronized (mConnection) {
+            if (mService != null) {
+                if (VDBG)
+                    Log.d(TAG, "Unbinding service...");
+                try {
+                    mAdapter.getBluetoothManager().
+                        unbindBluetoothProfileService(BluetoothProfile.LE_CALL_CONTROL,
+                        mConnection);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to unbind TelephoneBearerService", e);
+                } finally {
+                    mService = null;
+                }
+            }
+        }
+    }
+
+    /* package */ void close() {
+        if (VDBG)
+            log("close()");
+        unregisterBearer();
+
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException re) {
+                Log.e(TAG, "", re);
+            }
+        }
+        mServiceListener = null;
+        doUnbind();
+    }
+
+    private IBluetoothLeCallControl getService() {
+        return mService;
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public int getConnectionState(@Nullable BluetoothDevice device) {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Not supported
+     *
+     * @throws UnsupportedOperationException
+     */
+    @Override
+    public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(
+        @NonNull int[] states) {
+        throw new UnsupportedOperationException("not supported");
+    }
+
+    /**
+     * Register Telephone Bearer exposing the interface that allows remote devices
+     * to track and control the call states.
+     *
+     * <p>
+     * This is an asynchronous call. The callback is used to notify success or
+     * failure if the function returns true.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * <!-- The UCI is a String identifier of the telephone bearer as defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers/uniform-caller-identifiers
+     * (login required). -->
+     *
+     * <!-- The examples of common URI schemes can be found in
+     * https://iana.org/assignments/uri-schemes/uri-schemes.xhtml -->
+     *
+     * <!-- The Technology is an integer value. The possible values are defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+     * -->
+     *
+     * @param uci          Bearer Unique Client Identifier
+     * @param uriSchemes   URI Schemes supported list
+     * @param capabilities bearer capabilities
+     * @param provider     Network provider name
+     * @param technology   Network technology
+     * @param executor     {@link Executor} object on which callback will be
+     *                     executed. The Executor object is required.
+     * @param callback     {@link Callback} object to which callback messages will
+     *                     be sent. The Callback object is required.
+     * @return true on success, false otherwise
+     * @hide
+     */
+    @SuppressLint("ExecutorRegistration")
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean registerBearer(@Nullable String uci,
+                    @NonNull List<String> uriSchemes, int capabilities,
+                    @NonNull String provider, int technology,
+                    @NonNull Executor executor, @NonNull Callback callback) {
+        if (DBG) {
+            Log.d(TAG, "registerBearer");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("null parameter: " + callback);
+        }
+        if (mCcid != 0) {
+            return false;
+        }
+
+        mToken = uci;
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            if (mCallback != null) {
+                Log.e(TAG, "Bearer can be opened only once");
+                return false;
+            }
+
+            mCallback = callback;
+            try {
+                CallbackWrapper callbackWrapper = new CallbackWrapper(executor, callback);
+                service.registerBearer(mToken, callbackWrapper, uci, uriSchemes, capabilities,
+                                        provider, technology);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+                mCallback = null;
+                return false;
+            }
+
+            if (mCcid == 0) {
+                mCallback = null;
+                return false;
+            }
+
+            return true;
+        }
+
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return false;
+    }
+
+    /**
+     * Unregister Telephone Bearer Service and destroy all the associated data.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void unregisterBearer() {
+        if (DBG) {
+            Log.d(TAG, "unregisterBearer");
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        int ccid = mCcid;
+        mCcid = 0;
+        mCallback = null;
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.unregisterBearer(mToken);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Get the Content Control ID (CCID) value.
+     *
+     * @return ccid Content Control ID value
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public int getContentControlId() {
+        return mCcid;
+    }
+
+    /**
+     * Notify about the newly added call.
+     *
+     * <p>
+     * This shall be called as early as possible after the call has been added.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param call Newly added call
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallAdded(@NonNull BluetoothLeCall call) {
+        if (DBG) {
+            Log.d(TAG, "onCallAdded: call=" + call);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callAdded(mCcid, call);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Notify about the removed call.
+     *
+     * <p>
+     * This shall be called as early as possible after the call has been removed.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callId The Id of a call that has been removed
+     * @param reason Call termination reason
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallRemoved(@NonNull UUID callId, @TerminationReason int reason) {
+        if (DBG) {
+            Log.d(TAG, "callRemoved: callId=" + callId);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callRemoved(mCcid, new ParcelUuid(callId), reason);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Notify the call state change
+     *
+     * <p>
+     * This shall be called as early as possible after the state of the call has
+     * changed.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callId The call Id that state has been changed
+     * @param state  Call state
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void onCallStateChanged(@NonNull UUID callId, @BluetoothLeCall.State int state) {
+        if (DBG) {
+            Log.d(TAG, "callStateChanged: callId=" + callId + " state=" + state);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.callStateChanged(mCcid, new ParcelUuid(callId), state);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Provide the current calls list
+     *
+     * <p>
+     * This function must be invoked after registration if application has any
+     * calls.
+     *
+     * @param calls current calls list
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+     public void currentCallsList(@NonNull List<BluetoothLeCall> calls) {
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.currentCallsList(mCcid, calls);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
+    /**
+     * Provide the network current status
+     *
+     * <p>
+     * This function must be invoked on change of network state.
+     *
+     * <p>
+     * Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * <!-- The Technology is an integer value. The possible values are defined at
+     * https://www.bluetooth.com/specifications/assigned-numbers (login required).
+     * -->
+     *
+     * @param provider   Network provider name
+     * @param technology Network technology
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void networkStateChanged(@NonNull String provider, int technology) {
+        if (DBG) {
+            Log.d(TAG, "networkStateChanged: provider=" + provider + ", technology=" + technology);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.networkStateChanged(mCcid, provider, technology);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+    }
+
+    /**
+     * Send a response to a call control request to a remote device.
+     *
+     * <p>
+     * This function must be invoked in when a request is received by one of these
+     * callback methods:
+     *
+     * <ul>
+     * <li>{@link Callback#onAcceptCall}
+     * <li>{@link Callback#onTerminateCall}
+     * <li>{@link Callback#onHoldCall}
+     * <li>{@link Callback#onUnholdCall}
+     * <li>{@link Callback#onPlaceCall}
+     * <li>{@link Callback#onJoinCalls}
+     * </ul>
+     *
+     * @param requestId The ID of the request that was received with the callback
+     * @param result    The result of the request to be sent to the remote devices
+     */
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void requestResult(int requestId, @Result int result) {
+        if (DBG) {
+            Log.d(TAG, "requestResult: requestId=" + requestId + " result=" + result);
+        }
+        if (mCcid == 0) {
+            return;
+        }
+
+        final IBluetoothLeCallControl service = getService();
+        if (service != null) {
+            try {
+                service.requestResult(mCcid, requestId, result);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
+    private static boolean isValidDevice(@Nullable BluetoothDevice device) {
+        return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    private final IBluetoothProfileServiceConnection mConnection =
+                                    new IBluetoothProfileServiceConnection.Stub() {
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) {
+                Log.d(TAG, "Proxy object connected");
+            }
+            mService = IBluetoothLeCallControl.Stub.asInterface(Binder.allowBlocking(service));
+            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_CONNECTED));
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) {
+                Log.d(TAG, "Proxy object disconnected");
+            }
+            doUnbind();
+            mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_TBS_SERVICE_DISCONNECTED));
+        }
+    };
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case MESSAGE_TBS_SERVICE_CONNECTED: {
+                if (mServiceListener != null) {
+                    mServiceListener.onServiceConnected(BluetoothProfile.LE_CALL_CONTROL,
+                        BluetoothLeCallControl.this);
+                }
+                break;
+            }
+            case MESSAGE_TBS_SERVICE_DISCONNECTED: {
+                if (mServiceListener != null) {
+                    mServiceListener.onServiceDisconnected(BluetoothProfile.LE_CALL_CONTROL);
+                }
+                break;
+            }
+            }
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index e047e5d..d0f74e9 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -240,12 +240,19 @@
     int LE_AUDIO_BROADCAST = 26;
 
     /**
+     * @hide
+     * Telephone Bearer Service from Call Control Profile
+     *
+     */
+    int LE_CALL_CONTROL = 27;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    int MAX_PROFILE_ID = 26;
+    int MAX_PROFILE_ID = 27;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/le/AdvertiseSettings.java b/core/java/android/bluetooth/le/AdvertiseSettings.java
index 7129d76..c52a6ee 100644
--- a/core/java/android/bluetooth/le/AdvertiseSettings.java
+++ b/core/java/android/bluetooth/le/AdvertiseSettings.java
@@ -16,6 +16,9 @@
 
 package android.bluetooth.le;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.bluetooth.le.AdvertisingSetParameters.AddressTypeStatus;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -70,17 +73,21 @@
      */
     private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
 
+
     private final int mAdvertiseMode;
     private final int mAdvertiseTxPowerLevel;
     private final int mAdvertiseTimeoutMillis;
     private final boolean mAdvertiseConnectable;
+    private final int mOwnAddressType;
 
     private AdvertiseSettings(int advertiseMode, int advertiseTxPowerLevel,
-            boolean advertiseConnectable, int advertiseTimeout) {
+            boolean advertiseConnectable, int advertiseTimeout,
+            @AddressTypeStatus int ownAddressType) {
         mAdvertiseMode = advertiseMode;
         mAdvertiseTxPowerLevel = advertiseTxPowerLevel;
         mAdvertiseConnectable = advertiseConnectable;
         mAdvertiseTimeoutMillis = advertiseTimeout;
+        mOwnAddressType = ownAddressType;
     }
 
     private AdvertiseSettings(Parcel in) {
@@ -88,6 +95,7 @@
         mAdvertiseTxPowerLevel = in.readInt();
         mAdvertiseConnectable = in.readInt() != 0;
         mAdvertiseTimeoutMillis = in.readInt();
+        mOwnAddressType = in.readInt();
     }
 
     /**
@@ -118,12 +126,23 @@
         return mAdvertiseTimeoutMillis;
     }
 
+    /**
+     * @return the own address type for advertising
+     *
+     * @hide
+     */
+    @SystemApi
+    public @AddressTypeStatus int getOwnAddressType() {
+        return mOwnAddressType;
+    }
+
     @Override
     public String toString() {
         return "Settings [mAdvertiseMode=" + mAdvertiseMode
                 + ", mAdvertiseTxPowerLevel=" + mAdvertiseTxPowerLevel
                 + ", mAdvertiseConnectable=" + mAdvertiseConnectable
-                + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis + "]";
+                + ", mAdvertiseTimeoutMillis=" + mAdvertiseTimeoutMillis
+                + ", mOwnAddressType=" + mOwnAddressType + "]";
     }
 
     @Override
@@ -137,6 +156,7 @@
         dest.writeInt(mAdvertiseTxPowerLevel);
         dest.writeInt(mAdvertiseConnectable ? 1 : 0);
         dest.writeInt(mAdvertiseTimeoutMillis);
+        dest.writeInt(mOwnAddressType);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AdvertiseSettings> CREATOR =
@@ -160,6 +180,7 @@
         private int mTxPowerLevel = ADVERTISE_TX_POWER_MEDIUM;
         private int mTimeoutMillis = 0;
         private boolean mConnectable = true;
+        private int mOwnAddressType = AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT;
 
         /**
          * Set advertise mode to control the advertising power and latency.
@@ -226,10 +247,31 @@
         }
 
         /**
+         * Set own address type for advertising to control public or privacy mode. If used to set
+         * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+         * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+         * time of starting advertising.
+         *
+         * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+            if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+                    ||  ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+                throw new IllegalArgumentException("unknown address type " + ownAddressType);
+            }
+            mOwnAddressType = ownAddressType;
+            return this;
+        }
+
+        /**
          * Build the {@link AdvertiseSettings} object.
          */
         public AdvertiseSettings build() {
-            return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis);
+            return new AdvertiseSettings(mMode, mTxPowerLevel, mConnectable, mTimeoutMillis,
+                mOwnAddressType);
         }
     }
 }
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index e39b198..5c8fae6 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -16,11 +16,17 @@
 
 package android.bluetooth.le;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The {@link AdvertisingSetParameters} provide a way to adjust advertising
  * preferences for each
@@ -97,6 +103,39 @@
      */
     private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
 
+    /** @hide */
+    @IntDef(prefix = "ADDRESS_TYPE_", value = {
+        ADDRESS_TYPE_DEFAULT,
+        ADDRESS_TYPE_PUBLIC,
+        ADDRESS_TYPE_RANDOM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AddressTypeStatus {}
+
+    /**
+     * Advertise own address type that corresponds privacy settings of the device.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ADDRESS_TYPE_DEFAULT = -1;
+
+    /**
+     * Advertise own public address type.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ADDRESS_TYPE_PUBLIC = 0;
+
+    /**
+     * Generate and adverise own resolvable private address.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int ADDRESS_TYPE_RANDOM = 1;
+
     private final boolean mIsLegacy;
     private final boolean mIsAnonymous;
     private final boolean mIncludeTxPower;
@@ -106,11 +145,12 @@
     private final boolean mScannable;
     private final int mInterval;
     private final int mTxPowerLevel;
+    private final int mOwnAddressType;
 
     private AdvertisingSetParameters(boolean connectable, boolean scannable, boolean isLegacy,
             boolean isAnonymous, boolean includeTxPower,
             int primaryPhy, int secondaryPhy,
-            int interval, int txPowerLevel) {
+            int interval, int txPowerLevel, @AddressTypeStatus int ownAddressType) {
         mConnectable = connectable;
         mScannable = scannable;
         mIsLegacy = isLegacy;
@@ -120,6 +160,7 @@
         mSecondaryPhy = secondaryPhy;
         mInterval = interval;
         mTxPowerLevel = txPowerLevel;
+        mOwnAddressType = ownAddressType;
     }
 
     private AdvertisingSetParameters(Parcel in) {
@@ -132,6 +173,7 @@
         mSecondaryPhy = in.readInt();
         mInterval = in.readInt();
         mTxPowerLevel = in.readInt();
+        mOwnAddressType = in.readInt();
     }
 
     /**
@@ -197,6 +239,16 @@
         return mTxPowerLevel;
     }
 
+    /**
+     * @return the own address type for advertising
+     *
+     * @hide
+     */
+    @SystemApi
+    public @AddressTypeStatus int getOwnAddressType() {
+        return mOwnAddressType;
+    }
+
     @Override
     public String toString() {
         return "AdvertisingSetParameters [connectable=" + mConnectable
@@ -206,7 +258,8 @@
                 + ", primaryPhy=" + mPrimaryPhy
                 + ", secondaryPhy=" + mSecondaryPhy
                 + ", interval=" + mInterval
-                + ", txPowerLevel=" + mTxPowerLevel + "]";
+                + ", txPowerLevel=" + mTxPowerLevel
+                + ", ownAddressType=" + mOwnAddressType + "]";
     }
 
     @Override
@@ -225,6 +278,7 @@
         dest.writeInt(mSecondaryPhy);
         dest.writeInt(mInterval);
         dest.writeInt(mTxPowerLevel);
+        dest.writeInt(mOwnAddressType);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<AdvertisingSetParameters> CREATOR =
@@ -253,6 +307,7 @@
         private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
         private int mInterval = INTERVAL_LOW;
         private int mTxPowerLevel = TX_POWER_MEDIUM;
+        private int mOwnAddressType = ADDRESS_TYPE_DEFAULT;
 
         /**
          * Set whether the advertisement type should be connectable or
@@ -399,6 +454,26 @@
         }
 
         /**
+         * Set own address type for advertising to control public or privacy mode. If used to set
+         * address type anything other than {@link AdvertisingSetParameters#ADDRESS_TYPE_DEFAULT},
+         * then it will require BLUETOOTH_PRIVILEGED permission and will be checked at the
+         * time of starting advertising.
+         *
+         * @throws IllegalArgumentException If the {@code ownAddressType} is invalid
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setOwnAddressType(@AddressTypeStatus int ownAddressType) {
+            if (ownAddressType < AdvertisingSetParameters.ADDRESS_TYPE_DEFAULT
+                    ||  ownAddressType > AdvertisingSetParameters.ADDRESS_TYPE_RANDOM) {
+                throw new IllegalArgumentException("unknown address type " + ownAddressType);
+            }
+            mOwnAddressType = ownAddressType;
+            return this;
+        }
+
+        /**
          * Build the {@link AdvertisingSetParameters} object.
          *
          * @throws IllegalStateException if invalid combination of parameters is used.
@@ -431,7 +506,8 @@
             }
 
             return new AdvertisingSetParameters(mConnectable, mScannable, mIsLegacy, mIsAnonymous,
-                    mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel);
+                    mIncludeTxPower, mPrimaryPhy, mSecondaryPhy, mInterval, mTxPowerLevel,
+                    mOwnAddressType);
         }
     }
 }
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index b9f8a57..879dcee 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -138,6 +138,7 @@
             parameters.setLegacyMode(true);
             parameters.setConnectable(isConnectable);
             parameters.setScannable(true); // legacy advertisements we support are always scannable
+            parameters.setOwnAddressType(settings.getOwnAddressType());
             if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
                 parameters.setInterval(1600); // 1s
             } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 32827ae..b3435b1 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1182,7 +1182,7 @@
         public int match(Uri data, boolean wildcardSupported) {
             String host = data.getHost();
             if (host == null) {
-                if (wildcardSupported && mWild) {
+                if (wildcardSupported && mWild && mHost.isEmpty()) {
                     // special case, if no host is provided, but the Authority is wildcard, match
                     return MATCH_CATEGORY_HOST;
                 } else {
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 63fcb49..8fd41f2 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -505,7 +505,7 @@
         mChoiceValues = in.readStringArray();
         mCurrentValue = in.readString();
         mCurrentValues = in.readStringArray();
-        Parcelable[] parcelables = in.readParcelableArray(null);
+        Parcelable[] parcelables = in.readParcelableArray(null, RestrictionEntry.class);
         if (parcelables != null) {
             mRestrictions = new RestrictionEntry[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index 77b8be3..d04c97c 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -37,6 +37,9 @@
       "name": "CarrierAppIntegrationTestCases"
     },
     {
+      "name": "ApkVerityTest"
+    },
+    {
       "name": "CtsIncrementalInstallHostTestCases",
       "options": [
         {
@@ -135,6 +138,9 @@
           "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert"
         }
       ]
+    },
+    {
+      "name": "CtsInstallHostTestCases"
     }
   ]
 }
diff --git a/core/java/android/content/res/TEST_MAPPING b/core/java/android/content/res/TEST_MAPPING
index 535afd36..3703f2e 100644
--- a/core/java/android/content/res/TEST_MAPPING
+++ b/core/java/android/content/res/TEST_MAPPING
@@ -1,4 +1,12 @@
 {
+  "imports": [
+    {
+      "path": "frameworks/base/core/tests/coretests/src/android/content/res"
+    },
+    {
+      "path": "frameworks/base/core/tests/coretests/src/com/android/internal/content/res"
+    }
+  ],
   "presubmit": [
     {
       "name": "CtsResourcesLoaderTests"
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 7714dd8..243f801 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -38,6 +38,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_STATUS";
 
@@ -46,6 +47,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
 
@@ -59,6 +61,7 @@
      *
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
     public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
             "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 86ae3a3..5df64e3 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -599,7 +599,8 @@
 
         synchronized (mSurfacesLock) {
             mSurfaceSet.clear();
-            Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader());
+            Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader(),
+                    Surface.class);
             if (parcelableArray != null) {
                 for (Parcelable p : parcelableArray) {
                     Surface s = (Surface) p;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 6ea2ac4..4cc001a 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -436,7 +436,8 @@
             mNumAudioSources = in.readInt();
             mIsInitializationRequired = in.readInt() == 1;
             mIsCaptureSupported = in.readInt() == 1;
-            Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
+            Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader(),
+                    BandDescriptor.class);
             mBands = new BandDescriptor[tmp.length];
             for (int i = 0; i < tmp.length; i++) {
                 mBands[i] = (BandDescriptor) tmp[i];
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index afaa085..93573d1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -523,6 +523,7 @@
     private Handler mHandler;
     private boolean mImeSurfaceScheduledForRemoval;
     private ImsConfigurationTracker mConfigTracker = new ImsConfigurationTracker();
+    private boolean mDestroyed;
 
     /**
      * An opaque {@link Binder} token of window requesting {@link InputMethodImpl#showSoftInput}
@@ -599,6 +600,11 @@
         @Override
         public final void initializeInternal(@NonNull IBinder token,
                 IInputMethodPrivilegedOperations privilegedOperations, int configChanges) {
+            if (mDestroyed) {
+                Log.i(TAG, "The InputMethodService has already onDestroyed()."
+                    + "Ignore the initialization.");
+                return;
+            }
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
             mConfigTracker.onInitialize(configChanges);
             mPrivOps.set(privilegedOperations);
@@ -1435,6 +1441,7 @@
     }
 
     @Override public void onDestroy() {
+        mDestroyed = true;
         super.onDestroy();
         mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
                 mInsetsComputer);
@@ -1811,15 +1818,19 @@
     
     void updateExtractFrameVisibility() {
         final int vis;
+        updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
+
         if (isFullscreenMode()) {
             vis = mExtractViewHidden ? View.INVISIBLE : View.VISIBLE;
             // "vis" should be applied for the extract frame as well in the fullscreen mode.
             mExtractFrame.setVisibility(vis);
         } else {
-            vis = View.VISIBLE;
+            // mFullscreenArea visibility will according the candidate frame visibility once the
+            // extract frame is gone.
+            vis = mCandidatesVisibility;
             mExtractFrame.setVisibility(View.GONE);
         }
-        updateCandidatesVisibility(mCandidatesVisibility == View.VISIBLE);
+
         if (mDecorViewWasVisible && mFullscreenArea.getVisibility() != vis) {
             int animRes = mThemeAttrs.getResourceId(vis == View.VISIBLE
                     ? com.android.internal.R.styleable.InputMethodService_imeExtractEnterAnimation
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index fab692c..5554137 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -35,6 +35,7 @@
 import android.annotation.RequiresFeature;
 import android.content.pm.PackageManager;
 import android.security.Credentials;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.VpnProfile;
@@ -70,6 +71,7 @@
  *     Exchange, Version 2 (IKEv2)</a>
  */
 public final class Ikev2VpnProfile extends PlatformVpnProfile {
+    private static final String TAG = Ikev2VpnProfile.class.getSimpleName();
     /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
     public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
     /** Prefix for when a Private Key is stored directly in the profile @hide */
@@ -163,6 +165,10 @@
 
         // UnmodifiableList doesn't make a defensive copy by default.
         mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+        if (excludeLocalRoutes && !isBypassable) {
+            throw new IllegalArgumentException(
+                    "Vpn should be byassable if excludeLocalRoutes is set");
+        }
 
         mIsBypassable = isBypassable;
         mIsMetered = isMetered;
@@ -520,7 +526,10 @@
                 throw new IllegalArgumentException("Invalid auth method set");
         }
 
-        builder.setExcludeLocalRoutes(profile.excludeLocalRoutes);
+        if (profile.excludeLocalRoutes && !profile.isBypassable) {
+            Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
+        }
+        builder.setExcludeLocalRoutes(profile.excludeLocalRoutes && profile.isBypassable);
 
         return builder.build();
     }
@@ -907,9 +916,23 @@
         }
 
         /**
-         *  Sets whether the local traffic is exempted from the VPN.
+         * Sets whether the local traffic is exempted from the VPN.
          *
-         *  @hide TODO(184750836): unhide once the implementation is completed
+         * When this is set, the system will not use the VPN network when an app
+         * tries to send traffic for an IP address that is on a local network.
+         *
+         * Note that there are important security implications. In particular, the
+         * networks that the device connects to typically decides what IP addresses
+         * are part of the local network. This means that for VPNs setting this
+         * flag, it is possible for anybody to set up a public network in such a
+         * way that traffic to arbitrary IP addresses will bypass the VPN, including
+         * traffic to services like DNS. When using this API, please consider the
+         * security implications for your particular case.
+         *
+         * Note that because the local traffic will always bypass the VPN,
+         * it is not possible to set this flag on a non-bypassable VPN.
+         *
+         * @hide TODO(184750836): unhide once the implementation is completed
          */
         @NonNull
         @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
diff --git a/core/java/android/net/InternalNetworkUpdateRequest.java b/core/java/android/net/InternalNetworkUpdateRequest.java
index 6f09383..f42c4b7 100644
--- a/core/java/android/net/InternalNetworkUpdateRequest.java
+++ b/core/java/android/net/InternalNetworkUpdateRequest.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,7 +26,7 @@
 public final class InternalNetworkUpdateRequest implements Parcelable {
     @NonNull
     private final StaticIpConfiguration mIpConfig;
-    @Nullable
+    @NonNull
     private final NetworkCapabilities mNetworkCapabilities;
 
     @NonNull
@@ -37,20 +36,16 @@
 
     @NonNull
     public NetworkCapabilities getNetworkCapabilities() {
-        return mNetworkCapabilities == null
-                ? null : new NetworkCapabilities(mNetworkCapabilities);
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     /** @hide */
     public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
-            @Nullable final NetworkCapabilities networkCapabilities) {
+            @NonNull final NetworkCapabilities networkCapabilities) {
         Objects.requireNonNull(ipConfig);
+        Objects.requireNonNull(networkCapabilities);
         mIpConfig = new StaticIpConfiguration(ipConfig);
-        if (null == networkCapabilities) {
-            mNetworkCapabilities = null;
-        } else {
-            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
-        }
+        mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
     }
 
     private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2ced056..1ae1b05 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.NetworkUtilsInternal;
 import com.android.internal.net.VpnConfig;
 
@@ -50,6 +51,7 @@
 import java.net.InetAddress;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -471,6 +473,13 @@
         }
     }
 
+    private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) {
+        final IpPrefix prefix = new IpPrefix(address, prefixLength);
+        if (!prefix.getAddress().equals(address)) {
+            throw new IllegalArgumentException("Bad address");
+        }
+    }
+
     /**
      * Helper class to create a VPN interface. This class should be always
      * used within the scope of the outer {@link VpnService}.
@@ -481,9 +490,9 @@
 
         private final VpnConfig mConfig = new VpnConfig();
         @UnsupportedAppUsage
-        private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+        private final List<LinkAddress> mAddresses = new ArrayList<>();
         @UnsupportedAppUsage
-        private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+        private final List<RouteInfo> mRoutes = new ArrayList<>();
 
         public Builder() {
             mConfig.user = VpnService.this.getClass().getName();
@@ -555,7 +564,6 @@
                 throw new IllegalArgumentException("Bad address");
             }
             mAddresses.add(new LinkAddress(address, prefixLength));
-            mConfig.updateAllowedFamilies(address);
             return this;
         }
 
@@ -579,28 +587,68 @@
          * Add a network route to the VPN interface. Both IPv4 and IPv6
          * routes are supported.
          *
+         * If a route with the same destination is already present, its type will be updated.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        private Builder addRoute(@NonNull IpPrefix prefix, int type) {
+            check(prefix.getAddress(), prefix.getPrefixLength());
+
+            final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */
+                    null, /* interface */ null, type);
+
+            final int index = findRouteIndexByDestination(newRoute);
+
+            if (index == -1) {
+                mRoutes.add(newRoute);
+            } else {
+                mRoutes.set(index, newRoute);
+            }
+
+            return this;
+        }
+
+        /**
+         * Add a network route to the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
          * Adding a route implicitly allows traffic from that address family
          * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
          *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
          * @throws IllegalArgumentException if the route is invalid.
          */
         @NonNull
         public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
-            check(address, prefixLength);
+            checkNonPrefixBytes(address, prefixLength);
 
-            int offset = prefixLength / 8;
-            byte[] bytes = address.getAddress();
-            if (offset < bytes.length) {
-                for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
-                    if (bytes[offset] != 0) {
-                        throw new IllegalArgumentException("Bad address");
-                    }
-                }
-            }
-            mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
-                RouteInfo.RTN_UNICAST));
-            mConfig.updateAllowedFamilies(address);
-            return this;
+            return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST);
+        }
+
+        /**
+         * Add a network route to the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
+         * Adding a route implicitly allows traffic from that address family
+         * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+         *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        public Builder addRoute(@NonNull IpPrefix prefix) {
+            return addRoute(prefix, RouteInfo.RTN_UNICAST);
         }
 
         /**
@@ -611,6 +659,12 @@
          * Adding a route implicitly allows traffic from that address family
          * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
          *
+         * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
          * @throws IllegalArgumentException if the route is invalid.
          * @see #addRoute(InetAddress, int)
          */
@@ -620,6 +674,23 @@
         }
 
         /**
+         * Exclude a network route from the VPN interface. Both IPv4 and IPv6
+         * routes are supported.
+         *
+         * Calling this method overrides previous calls to {@link #addRoute} for the same
+         * destination.
+         *
+         * If multiple routes match the packet destination, route with the longest prefix takes
+         * precedence.
+         *
+         * @throws IllegalArgumentException if the route is invalid.
+         */
+        @NonNull
+        public Builder excludeRoute(@NonNull IpPrefix prefix) {
+            return addRoute(prefix, RouteInfo.RTN_THROW);
+        }
+
+        /**
          * Add a DNS server to the VPN connection. Both IPv4 and IPv6
          * addresses are supported. If none is set, the DNS servers of
          * the default network will be used.
@@ -900,5 +971,23 @@
                 throw new IllegalStateException(e);
             }
         }
+
+        private int findRouteIndexByDestination(RouteInfo route) {
+            for (int i = 0; i < mRoutes.size(); i++) {
+                if (mRoutes.get(i).getDestination().equals(route.getDestination())) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /**
+         * Method for testing, to observe mRoutes while builder is being used.
+         * @hide
+         */
+        @VisibleForTesting
+        public List<RouteInfo> routes() {
+            return Collections.unmodifiableList(mRoutes);
+        }
     }
 }
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 1ac3f0a..125b573 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -15,6 +15,9 @@
  */
 package android.net.vcn;
 
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
 import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
@@ -23,8 +26,12 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
+import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
 import android.os.PersistableBundle;
 import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
 
@@ -37,32 +44,36 @@
 import java.util.Objects;
 import java.util.Set;
 
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying cellular
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
 public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
     private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
     @NonNull private final Set<String> mAllowedNetworkPlmnIds;
     private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
     @NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
 
-    private static final String ALLOW_ROAMING_KEY = "mAllowRoaming";
-    private final boolean mAllowRoaming;
+    private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+    private final int mRoamingMatchCriteria;
 
-    private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
-    private final boolean mRequireOpportunistic;
+    private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+    private final int mOpportunisticMatchCriteria;
 
     private VcnCellUnderlyingNetworkTemplate(
             int networkQuality,
-            boolean allowMetered,
+            int meteredMatchCriteria,
             Set<String> allowedNetworkPlmnIds,
             Set<Integer> allowedSpecificCarrierIds,
-            boolean allowRoaming,
-            boolean requireOpportunistic) {
-        super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered);
+            int roamingMatchCriteria,
+            int opportunisticMatchCriteria) {
+        super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria);
         mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
         mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
-        mAllowRoaming = allowRoaming;
-        mRequireOpportunistic = requireOpportunistic;
+        mRoamingMatchCriteria = roamingMatchCriteria;
+        mOpportunisticMatchCriteria = opportunisticMatchCriteria;
 
         validate();
     }
@@ -72,15 +83,17 @@
     protected void validate() {
         super.validate();
         validatePlmnIds(mAllowedNetworkPlmnIds);
-        Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null");
+        Objects.requireNonNull(mAllowedSpecificCarrierIds, "matchingCarrierIds is null");
+        validateMatchCriteria(mRoamingMatchCriteria, "mRoamingMatchCriteria");
+        validateMatchCriteria(mOpportunisticMatchCriteria, "mOpportunisticMatchCriteria");
     }
 
-    private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) {
-        Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null");
+    private static void validatePlmnIds(Set<String> matchingOperatorPlmnIds) {
+        Objects.requireNonNull(matchingOperatorPlmnIds, "matchingOperatorPlmnIds is null");
 
         // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
         // digits.
-        for (String id : allowedNetworkPlmnIds) {
+        for (String id : matchingOperatorPlmnIds) {
             if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
                 continue;
             } else {
@@ -97,7 +110,7 @@
         Objects.requireNonNull(in, "PersistableBundle is null");
 
         final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
-        final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
+        final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
 
         final PersistableBundle plmnIdsBundle =
                 in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
@@ -114,16 +127,16 @@
                         PersistableBundleUtils.toList(
                                 specificCarrierIdsBundle, INTEGER_DESERIALIZER));
 
-        final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
-        final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
+        final int roamingMatchCriteria = in.getInt(ROAMING_MATCH_KEY);
+        final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
 
         return new VcnCellUnderlyingNetworkTemplate(
                 networkQuality,
-                allowMetered,
+                meteredMatchCriteria,
                 allowedNetworkPlmnIds,
                 allowedSpecificCarrierIds,
-                allowRoaming,
-                requireOpportunistic);
+                roamingMatchCriteria,
+                opportunisticMatchCriteria);
     }
 
     /** @hide */
@@ -143,35 +156,51 @@
                         new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
         result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
 
-        result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming);
-        result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic);
+        result.putInt(ROAMING_MATCH_KEY, mRoamingMatchCriteria);
+        result.putInt(OPPORTUNISTIC_MATCH_KEY, mOpportunisticMatchCriteria);
 
         return result;
     }
 
-    /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
+    /**
+     * Retrieve the matching operator PLMN IDs, or an empty set if any PLMN ID is acceptable.
+     *
+     * @see Builder#setOperatorPlmnIds(Set)
+     */
     @NonNull
-    public Set<String> getAllowedOperatorPlmnIds() {
+    public Set<String> getOperatorPlmnIds() {
         return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
     }
 
     /**
-     * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is
-     * acceptable.
+     * Retrieve the matching sim specific carrier IDs, or an empty set if any sim specific carrier
+     * ID is acceptable.
+     *
+     * @see Builder#setSimSpecificCarrierIds(Set)
      */
     @NonNull
-    public Set<Integer> getAllowedSpecificCarrierIds() {
+    public Set<Integer> getSimSpecificCarrierIds() {
         return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
     }
 
-    /** Return if roaming is allowed. */
-    public boolean allowRoaming() {
-        return mAllowRoaming;
+    /**
+     * Return the matching criteria for roaming networks.
+     *
+     * @see Builder#setRoaming(int)
+     */
+    @MatchCriteria
+    public int getRoaming() {
+        return mRoamingMatchCriteria;
     }
 
-    /** Return if requiring an opportunistic network. */
-    public boolean requireOpportunistic() {
-        return mRequireOpportunistic;
+    /**
+     * Return the matching criteria for opportunistic cellular subscriptions.
+     *
+     * @see Builder#setOpportunistic(int)
+     */
+    @MatchCriteria
+    public int getOpportunistic() {
+        return mOpportunisticMatchCriteria;
     }
 
     @Override
@@ -180,8 +209,8 @@
                 super.hashCode(),
                 mAllowedNetworkPlmnIds,
                 mAllowedSpecificCarrierIds,
-                mAllowRoaming,
-                mRequireOpportunistic);
+                mRoamingMatchCriteria,
+                mOpportunisticMatchCriteria);
     }
 
     @Override
@@ -197,8 +226,8 @@
         final VcnCellUnderlyingNetworkTemplate rhs = (VcnCellUnderlyingNetworkTemplate) other;
         return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
                 && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
-                && mAllowRoaming == rhs.mAllowRoaming
-                && mRequireOpportunistic == rhs.mRequireOpportunistic;
+                && mRoamingMatchCriteria == rhs.mRoamingMatchCriteria
+                && mOpportunisticMatchCriteria == rhs.mOpportunisticMatchCriteria;
     }
 
     /** @hide */
@@ -206,77 +235,137 @@
     void dumpTransportSpecificFields(IndentingPrintWriter pw) {
         pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
         pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
-        pw.println("mAllowRoaming: " + mAllowRoaming);
-        pw.println("mRequireOpportunistic: " + mRequireOpportunistic);
+        pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+        pw.println(
+                "mOpportunisticMatchCriteria: "
+                        + getMatchCriteriaString(mOpportunisticMatchCriteria));
     }
 
-    /** This class is used to incrementally build WifiNetworkPriority objects. */
-    public static final class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
+    /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
+    public static final class Builder {
+        private int mNetworkQuality = NETWORK_QUALITY_ANY;
+        private int mMeteredMatchCriteria = MATCH_ANY;
+
         @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
         @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
 
-        private boolean mAllowRoaming = false;
-        private boolean mRequireOpportunistic = false;
+        private int mRoamingMatchCriteria = MATCH_ANY;
+        private int mOpportunisticMatchCriteria = MATCH_ANY;
 
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * Set allowed operator PLMN IDs.
+         * Set the required network quality to match this template.
+         *
+         * <p>Network quality is a aggregation of multiple signals that reflect the network link
+         * metrics. For example, the network validation bit (see {@link
+         * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
+         * and signal strength.
+         *
+         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
+         * @hide
+         */
+        @NonNull
+        public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
+            validateNetworkQuality(networkQuality);
+
+            mNetworkQuality = networkQuality;
+            return this;
+        }
+
+        /**
+         * Set the matching criteria for metered networks.
+         *
+         * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+         * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+         * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+         *
+         * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+         */
+        // The matching getter is defined in the super class. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMetered()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setMetered(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setMetered");
+
+            mMeteredMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set operator PLMN IDs with which a network can match this template.
          *
          * <p>This is used to distinguish cases where roaming agreements may dictate a different
          * priority from a partner's networks.
          *
-         * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an
-         *     empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and
-         *     thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()}
-         *     and {@link SubscriptionInfo#getMncString()}.
+         * @param operatorPlmnIds the matching operator PLMN IDs in String. Network with one of the
+         *     matching PLMN IDs can match this template. If the set is empty, any PLMN ID will
+         *     match. The default is an empty set. A valid PLMN is a concatenation of MNC and MCC,
+         *     and thus consists of 5 or 6 decimal digits.
+         * @see SubscriptionInfo#getMccString()
+         * @see SubscriptionInfo#getMncString()
          */
         @NonNull
-        public Builder setAllowedOperatorPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
-            validatePlmnIds(allowedNetworkPlmnIds);
+        public Builder setOperatorPlmnIds(@NonNull Set<String> operatorPlmnIds) {
+            validatePlmnIds(operatorPlmnIds);
 
             mAllowedNetworkPlmnIds.clear();
-            mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds);
+            mAllowedNetworkPlmnIds.addAll(operatorPlmnIds);
             return this;
         }
 
         /**
-         * Set allowed specific carrier IDs.
+         * Set sim specific carrier IDs with which a network can match this template.
          *
-         * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty
-         *     set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}.
+         * @param simSpecificCarrierIds the matching sim specific carrier IDs. Network with one of
+         *     the sim specific carrier IDs can match this template. If the set is empty, any
+         *     carrier ID will match. The default is an empty set.
+         * @see TelephonyManager#getSimSpecificCarrierId()
          */
         @NonNull
-        public Builder setAllowedSpecificCarrierIds(
-                @NonNull Set<Integer> allowedSpecificCarrierIds) {
-            Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null");
+        public Builder setSimSpecificCarrierIds(@NonNull Set<Integer> simSpecificCarrierIds) {
+            Objects.requireNonNull(simSpecificCarrierIds, "simSpecificCarrierIds is null");
+
             mAllowedSpecificCarrierIds.clear();
-            mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds);
+            mAllowedSpecificCarrierIds.addAll(simSpecificCarrierIds);
             return this;
         }
 
         /**
-         * Set if roaming is allowed.
+         * Set the matching criteria for roaming networks.
          *
-         * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code
-         *     false}.
+         * <p>A template where setRoaming(MATCH_REQUIRED) will only match roaming networks (one
+         * without NET_CAPABILITY_NOT_ROAMING). A template where setRoaming(MATCH_FORBIDDEN) will
+         * only match a network that is not roaming (one with NET_CAPABILITY_NOT_ROAMING).
+         *
+         * @param matchCriteria the matching criteria for roaming networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING
          */
         @NonNull
-        public Builder setAllowRoaming(boolean allowRoaming) {
-            mAllowRoaming = allowRoaming;
+        public Builder setRoaming(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setRoaming");
+
+            mRoamingMatchCriteria = matchCriteria;
             return this;
         }
 
         /**
-         * Set if requiring an opportunistic network.
+         * Set the matching criteria for opportunistic cellular subscriptions.
          *
-         * @param requireOpportunistic the flag to indicate if caller requires an opportunistic
-         *     network. Defaults to {@code false}.
+         * @param matchCriteria the matching criteria for opportunistic cellular subscriptions.
+         *     Defaults to {@link #MATCH_ANY}.
+         * @see SubscriptionManager#setOpportunistic(boolean, int)
          */
         @NonNull
-        public Builder setRequireOpportunistic(boolean requireOpportunistic) {
-            mRequireOpportunistic = requireOpportunistic;
+        public Builder setOpportunistic(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setOpportunistic");
+
+            mOpportunisticMatchCriteria = matchCriteria;
             return this;
         }
 
@@ -285,17 +374,11 @@
         public VcnCellUnderlyingNetworkTemplate build() {
             return new VcnCellUnderlyingNetworkTemplate(
                     mNetworkQuality,
-                    mAllowMetered,
+                    mMeteredMatchCriteria,
                     mAllowedNetworkPlmnIds,
                     mAllowedSpecificCarrierIds,
-                    mAllowRoaming,
-                    mRequireOpportunistic);
-        }
-
-        /** @hide */
-        @Override
-        Builder self() {
-            return this;
+                    mRoamingMatchCriteria,
+                    mOpportunisticMatchCriteria);
         }
     }
 }
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d07c24a..92956e8 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,6 +16,7 @@
 package android.net.vcn;
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -42,7 +43,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
@@ -162,30 +163,24 @@
 
     /** @hide */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    public static final LinkedHashSet<VcnUnderlyingNetworkTemplate>
-            DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
+    public static final List<VcnUnderlyingNetworkTemplate> DEFAULT_UNDERLYING_NETWORK_TEMPLATES =
+            new ArrayList<>();
 
     static {
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setAllowRoaming(true /* allowRoaming */)
-                        .setRequireOpportunistic(true /* requireOpportunistic */)
+                        .setOpportunistic(MATCH_REQUIRED)
                         .build());
 
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
                         .build());
 
-        DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+        DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setAllowRoaming(true /* allowRoaming */)
-                        .setRequireOpportunistic(false /* requireOpportunistic */)
                         .build());
     }
 
@@ -200,9 +195,9 @@
 
     /** @hide */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    public static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+    public static final String UNDERLYING_NETWORK_TEMPLATES_KEY = "mUnderlyingNetworkTemplates";
 
-    @NonNull private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities;
+    @NonNull private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates;
 
     private static final String MAX_MTU_KEY = "mMaxMtu";
     private final int mMaxMtu;
@@ -215,7 +210,7 @@
             @NonNull String gatewayConnectionName,
             @NonNull IkeTunnelConnectionParams tunnelConnectionParams,
             @NonNull Set<Integer> exposedCapabilities,
-            @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+            @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             @NonNull long[] retryIntervalsMs,
             @IntRange(from = MIN_MTU_V6) int maxMtu) {
         mGatewayConnectionName = gatewayConnectionName;
@@ -224,9 +219,9 @@
         mRetryIntervalsMs = retryIntervalsMs;
         mMaxMtu = maxMtu;
 
-        mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities);
-        if (mUnderlyingNetworkPriorities.isEmpty()) {
-            mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+        mUnderlyingNetworkTemplates = new ArrayList<>(underlyingNetworkTemplates);
+        if (mUnderlyingNetworkTemplates.isEmpty()) {
+            mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
         }
 
         validate();
@@ -250,22 +245,19 @@
         mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
                 exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
 
-        final PersistableBundle networkPrioritiesBundle =
-                in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
+        final PersistableBundle networkTemplatesBundle =
+                in.getPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY);
 
-        if (networkPrioritiesBundle == null) {
-            // UNDERLYING_NETWORK_PRIORITIES_KEY was added in Android T. Thus
+        if (networkTemplatesBundle == null) {
+            // UNDERLYING_NETWORK_TEMPLATES_KEY was added in Android T. Thus
             // VcnGatewayConnectionConfig created on old platforms will not have this data and will
             // be assigned with the default value
-            mUnderlyingNetworkPriorities =
-                    new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
-
+            mUnderlyingNetworkTemplates = new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
         } else {
-            mUnderlyingNetworkPriorities =
-                    new LinkedHashSet<>(
-                            PersistableBundleUtils.toList(
-                                    networkPrioritiesBundle,
-                                    VcnUnderlyingNetworkTemplate::fromPersistableBundle));
+            mUnderlyingNetworkTemplates =
+                    PersistableBundleUtils.toList(
+                            networkTemplatesBundle,
+                            VcnUnderlyingNetworkTemplate::fromPersistableBundle);
         }
 
         mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
@@ -285,7 +277,7 @@
             checkValidCapability(cap);
         }
 
-        Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+        validateNetworkTemplateList(mUnderlyingNetworkTemplates);
         Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
         validateRetryInterval(mRetryIntervalsMs);
 
@@ -314,6 +306,19 @@
         }
     }
 
+    private static void validateNetworkTemplateList(
+            List<VcnUnderlyingNetworkTemplate> networkPriorityRules) {
+        Objects.requireNonNull(networkPriorityRules, "networkPriorityRules is null");
+
+        Set<VcnUnderlyingNetworkTemplate> existingRules = new ArraySet<>();
+        for (VcnUnderlyingNetworkTemplate rule : networkPriorityRules) {
+            Objects.requireNonNull(rule, "Found null value VcnUnderlyingNetworkTemplate");
+            if (!existingRules.add(rule)) {
+                throw new IllegalArgumentException("Found duplicate VcnUnderlyingNetworkTemplate");
+            }
+        }
+    }
+
     /**
      * Returns the configured Gateway Connection name.
      *
@@ -368,15 +373,13 @@
     }
 
     /**
-     * Retrieve the configured VcnUnderlyingNetworkTemplate list, or a default list if it is not
-     * configured.
+     * Retrieve the VcnUnderlyingNetworkTemplate list, or a default list if it is not configured.
      *
-     * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkTemplate>)
-     * @hide
+     * @see Builder#setVcnUnderlyingNetworkPriorities(List)
      */
     @NonNull
-    public LinkedHashSet<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
-        return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
+    public List<VcnUnderlyingNetworkTemplate> getVcnUnderlyingNetworkPriorities() {
+        return new ArrayList<>(mUnderlyingNetworkTemplates);
     }
 
     /**
@@ -415,15 +418,15 @@
                 PersistableBundleUtils.fromList(
                         new ArrayList<>(mExposedCapabilities),
                         PersistableBundleUtils.INTEGER_SERIALIZER);
-        final PersistableBundle networkPrioritiesBundle =
+        final PersistableBundle networkTemplatesBundle =
                 PersistableBundleUtils.fromList(
-                        new ArrayList<>(mUnderlyingNetworkPriorities),
+                        mUnderlyingNetworkTemplates,
                         VcnUnderlyingNetworkTemplate::toPersistableBundle);
 
         result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
         result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
         result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
-        result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle);
+        result.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, networkTemplatesBundle);
         result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
         result.putInt(MAX_MTU_KEY, mMaxMtu);
 
@@ -436,7 +439,7 @@
                 mGatewayConnectionName,
                 mTunnelConnectionParams,
                 mExposedCapabilities,
-                mUnderlyingNetworkPriorities,
+                mUnderlyingNetworkTemplates,
                 Arrays.hashCode(mRetryIntervalsMs),
                 mMaxMtu);
     }
@@ -451,7 +454,7 @@
         return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
                 && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
                 && mExposedCapabilities.equals(rhs.mExposedCapabilities)
-                && mUnderlyingNetworkPriorities.equals(rhs.mUnderlyingNetworkPriorities)
+                && mUnderlyingNetworkTemplates.equals(rhs.mUnderlyingNetworkTemplates)
                 && Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
                 && mMaxMtu == rhs.mMaxMtu;
     }
@@ -465,8 +468,8 @@
         @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
 
         @NonNull
-        private final LinkedHashSet<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkPriorities =
-                new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+        private final List<VcnUnderlyingNetworkTemplate> mUnderlyingNetworkTemplates =
+                new ArrayList<>(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
 
         @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
         private int mMaxMtu = DEFAULT_MAX_MTU;
@@ -539,27 +542,37 @@
         }
 
         /**
-         * Set the VcnUnderlyingNetworkTemplate list.
+         * Set the list of templates to match underlying networks against, in high-to-low priority
+         * order.
          *
-         * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
-         *     are ordered from most to least preferred, or an empty list to use the default
-         *     prioritization. The default network prioritization is Opportunistic cellular, Carrier
-         *     WiFi and Macro cellular
-         * @return
+         * <p>To select the VCN underlying network, the VCN connection will go through all the
+         * network candidates and return a network matching the highest priority rule.
+         *
+         * <p>If multiple networks match the same rule, the VCN will prefer an already-selected
+         * network as opposed to a new/unselected network. However, if both are new/unselected
+         * networks, a network will be chosen arbitrarily amongst the networks matching the highest
+         * priority rule.
+         *
+         * <p>If all networks fail to match the rules provided, an underlying network will still be
+         * selected (at random if necessary).
+         *
+         * @param underlyingNetworkTemplates a list of unique VcnUnderlyingNetworkTemplates that are
+         *     ordered from most to least preferred, or an empty list to use the default
+         *     prioritization. The default network prioritization order is Opportunistic cellular,
+         *     Carrier WiFi and then Macro cellular.
+         * @return this {@link Builder} instance, for chaining
          */
-        /** @hide */
         @NonNull
         public Builder setVcnUnderlyingNetworkPriorities(
-                @NonNull LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities) {
-            Objects.requireNonNull(
-                    mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+                @NonNull List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates) {
+            validateNetworkTemplateList(underlyingNetworkTemplates);
 
-            mUnderlyingNetworkPriorities.clear();
+            mUnderlyingNetworkTemplates.clear();
 
-            if (underlyingNetworkPriorities.isEmpty()) {
-                mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+            if (underlyingNetworkTemplates.isEmpty()) {
+                mUnderlyingNetworkTemplates.addAll(DEFAULT_UNDERLYING_NETWORK_TEMPLATES);
             } else {
-                mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities);
+                mUnderlyingNetworkTemplates.addAll(underlyingNetworkTemplates);
             }
 
             return this;
@@ -629,7 +642,7 @@
                     mGatewayConnectionName,
                     mTunnelConnectionParams,
                     mExposedCapabilities,
-                    mUnderlyingNetworkPriorities,
+                    mUnderlyingNetworkTemplates,
                     mRetryIntervalsMs,
                     mMaxMtu);
         }
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index d306d5c..60fc936 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -15,6 +15,8 @@
  */
 package android.net.vcn;
 
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 
 import android.annotation.IntDef;
@@ -31,17 +33,24 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a template containing set of underlying network requirements for doing
+ * route selection.
+ *
+ * <p>Apps provisioning a VCN can configure the underlying network priority for each Gateway
+ * Connection by setting a list (in priority order, most to least preferred) of the appropriate
+ * subclasses in the VcnGatewayConnectionConfig. See {@link
+ * VcnGatewayConnectionConfig.Builder#setVcnUnderlyingNetworkPriorities}
+ */
 public abstract class VcnUnderlyingNetworkTemplate {
     /** @hide */
-    protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
+    static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
     /** @hide */
-    protected static final int NETWORK_PRIORITY_TYPE_CELL = 2;
+    static final int NETWORK_PRIORITY_TYPE_CELL = 2;
 
-    /** Denotes that any network quality is acceptable */
+    /** Denotes that any network quality is acceptable. @hide */
     public static final int NETWORK_QUALITY_ANY = 0;
-    /** Denotes that network quality needs to be OK */
+    /** Denotes that network quality needs to be OK. @hide */
     public static final int NETWORK_QUALITY_OK = 100000;
 
     private static final SparseArray<String> NETWORK_QUALITY_TO_STRING_MAP = new SparseArray<>();
@@ -56,34 +65,82 @@
     @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
     public @interface NetworkQuality {}
 
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that networks with or without the
+     * characteristic are both acceptable to match this template.
+     */
+    public static final int MATCH_ANY = 0;
+
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that a network MUST have the
+     * capability in order to match this template.
+     */
+    public static final int MATCH_REQUIRED = 1;
+
+    /**
+     * Used to configure the matching criteria of a network characteristic. This may include network
+     * capabilities, or cellular subscription information. Denotes that a network MUST NOT have the
+     * capability in order to match this template.
+     */
+    public static final int MATCH_FORBIDDEN = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({MATCH_ANY, MATCH_REQUIRED, MATCH_FORBIDDEN})
+    public @interface MatchCriteria {}
+
+    private static final SparseArray<String> MATCH_CRITERIA_TO_STRING_MAP = new SparseArray<>();
+
+    static {
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_ANY, "MATCH_ANY");
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_REQUIRED, "MATCH_REQUIRED");
+        MATCH_CRITERIA_TO_STRING_MAP.put(MATCH_FORBIDDEN, "MATCH_FORBIDDEN");
+    }
+
     private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
     private final int mNetworkPriorityType;
 
     /** @hide */
-    protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
+    static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
+
     private final int mNetworkQuality;
 
     /** @hide */
-    protected static final String ALLOW_METERED_KEY = "mAllowMetered";
-    private final boolean mAllowMetered;
+    static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
+
+    private final int mMeteredMatchCriteria;
 
     /** @hide */
-    protected VcnUnderlyingNetworkTemplate(
-            int networkPriorityType, int networkQuality, boolean allowMetered) {
+    VcnUnderlyingNetworkTemplate(
+            int networkPriorityType, int networkQuality, int meteredMatchCriteria) {
         mNetworkPriorityType = networkPriorityType;
         mNetworkQuality = networkQuality;
-        mAllowMetered = allowMetered;
+        mMeteredMatchCriteria = meteredMatchCriteria;
     }
 
-    private static void validateNetworkQuality(int networkQuality) {
+    /** @hide */
+    static void validateNetworkQuality(int networkQuality) {
         Preconditions.checkArgument(
                 networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
                 "Invalid networkQuality:" + networkQuality);
     }
 
     /** @hide */
+    static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) {
+        Preconditions.checkArgument(
+                MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria),
+                "Invalid matching criteria: "
+                        + meteredMatchCriteria
+                        + " for "
+                        + matchingCapability);
+    }
+
+    /** @hide */
     protected void validate() {
         validateNetworkQuality(mNetworkQuality);
+        validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
     }
 
     /** @hide */
@@ -112,14 +169,14 @@
 
         result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
         result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
-        result.putBoolean(ALLOW_METERED_KEY, mAllowMetered);
+        result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
 
         return result;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered);
+        return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria);
     }
 
     @Override
@@ -131,7 +188,17 @@
         final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
         return mNetworkPriorityType == rhs.mNetworkPriorityType
                 && mNetworkQuality == rhs.mNetworkQuality
-                && mAllowMetered == rhs.mAllowMetered;
+                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria;
+    }
+
+    /** @hide */
+    static String getNameString(SparseArray<String> toStringMap, int key) {
+        return toStringMap.get(key, "Invalid value " + key);
+    }
+
+    /** @hide */
+    static String getMatchCriteriaString(int meteredMatchCriteria) {
+        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria);
     }
 
     /** @hide */
@@ -148,65 +215,32 @@
 
         pw.println(
                 "mNetworkQuality: "
-                        + NETWORK_QUALITY_TO_STRING_MAP.get(
-                                mNetworkQuality, "Invalid value " + mNetworkQuality));
-        pw.println("mAllowMetered: " + mAllowMetered);
+                        + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality));
+        pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
         dumpTransportSpecificFields(pw);
 
         pw.decreaseIndent();
     }
 
-    /** Retrieve the required network quality. */
+    /**
+     * Retrieve the required network quality to match this template.
+     *
+     * @see Builder#setNetworkQuality(int)
+     * @hide
+     */
     @NetworkQuality
     public int getNetworkQuality() {
         return mNetworkQuality;
     }
 
-    /** Return if a metered network is allowed. */
-    public boolean allowMetered() {
-        return mAllowMetered;
-    }
-
     /**
-     * This class is used to incrementally build VcnUnderlyingNetworkTemplate objects.
+     * Return the matching criteria for metered networks.
      *
-     * @param <T> The subclass to be built.
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
      */
-    public abstract static class Builder<T extends Builder<T>> {
-        /** @hide */
-        protected int mNetworkQuality = NETWORK_QUALITY_ANY;
-        /** @hide */
-        protected boolean mAllowMetered = false;
-
-        /** @hide */
-        protected Builder() {}
-
-        /**
-         * Set the required network quality.
-         *
-         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
-         */
-        @NonNull
-        public T setNetworkQuality(@NetworkQuality int networkQuality) {
-            validateNetworkQuality(networkQuality);
-
-            mNetworkQuality = networkQuality;
-            return self();
-        }
-
-        /**
-         * Set if a metered network is allowed.
-         *
-         * @param allowMetered the flag to indicate if a metered network is allowed, defaults to
-         *     {@code false}
-         */
-        @NonNull
-        public T setAllowMetered(boolean allowMetered) {
-            mAllowMetered = allowMetered;
-            return self();
-        }
-
-        /** @hide */
-        abstract T self();
+    @MatchCriteria
+    public int getMetered() {
+        return mMeteredMatchCriteria;
     }
 }
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 6bbb2bf..272ca9d 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -16,31 +16,59 @@
 package android.net.vcn;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.net.NetworkCapabilities;
 import android.os.PersistableBundle;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Objects;
+import java.util.Set;
 
-// TODO: Add documents
-/** @hide */
+/**
+ * This class represents a configuration for a network template class of underlying Carrier WiFi
+ * networks.
+ *
+ * <p>See {@link VcnUnderlyingNetworkTemplate}
+ */
 public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetworkTemplate {
-    private static final String SSID_KEY = "mSsid";
-    @Nullable private final String mSsid;
+    private static final String SSIDS_KEY = "mSsids";
+    @Nullable private final Set<String> mSsids;
 
     private VcnWifiUnderlyingNetworkTemplate(
-            int networkQuality, boolean allowMetered, String ssid) {
-        super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
-        mSsid = ssid;
+            int networkQuality, int meteredMatchCriteria, Set<String> ssids) {
+        super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria);
+        mSsids = new ArraySet<>(ssids);
 
         validate();
     }
 
     /** @hide */
+    @Override
+    protected void validate() {
+        super.validate();
+        validateSsids(mSsids);
+    }
+
+    private static void validateSsids(Set<String> ssids) {
+        Objects.requireNonNull(ssids, "ssids is null");
+
+        for (String ssid : ssids) {
+            Objects.requireNonNull(ssid, "found null value ssid");
+        }
+    }
+
+    /** @hide */
     @NonNull
     @VisibleForTesting(visibility = Visibility.PROTECTED)
     public static VcnWifiUnderlyingNetworkTemplate fromPersistableBundle(
@@ -48,9 +76,14 @@
         Objects.requireNonNull(in, "PersistableBundle is null");
 
         final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
-        final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
-        final String ssid = in.getString(SSID_KEY);
-        return new VcnWifiUnderlyingNetworkTemplate(networkQuality, allowMetered, ssid);
+        final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
+
+        final PersistableBundle ssidsBundle = in.getPersistableBundle(SSIDS_KEY);
+        Objects.requireNonNull(ssidsBundle, "ssidsBundle is null");
+        final Set<String> ssids =
+                new ArraySet<String>(
+                        PersistableBundleUtils.toList(ssidsBundle, STRING_DESERIALIZER));
+        return new VcnWifiUnderlyingNetworkTemplate(networkQuality, meteredMatchCriteria, ssids);
     }
 
     /** @hide */
@@ -59,13 +92,17 @@
     @VisibleForTesting(visibility = Visibility.PROTECTED)
     public PersistableBundle toPersistableBundle() {
         final PersistableBundle result = super.toPersistableBundle();
-        result.putString(SSID_KEY, mSsid);
+
+        final PersistableBundle ssidsBundle =
+                PersistableBundleUtils.fromList(new ArrayList<>(mSsids), STRING_SERIALIZER);
+        result.putPersistableBundle(SSIDS_KEY, ssidsBundle);
+
         return result;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), mSsid);
+        return Objects.hash(super.hashCode(), mSsids);
     }
 
     @Override
@@ -79,49 +116,95 @@
         }
 
         final VcnWifiUnderlyingNetworkTemplate rhs = (VcnWifiUnderlyingNetworkTemplate) other;
-        return mSsid.equals(rhs.mSsid);
+        return mSsids.equals(rhs.mSsids);
     }
 
     /** @hide */
     @Override
     void dumpTransportSpecificFields(IndentingPrintWriter pw) {
-        pw.println("mSsid: " + mSsid);
+        pw.println("mSsids: " + mSsids);
     }
 
-    /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */
-    @Nullable
-    public String getSsid() {
-        return mSsid;
+    /**
+     * Retrieve the matching SSIDs, or an empty set if any SSID is acceptable.
+     *
+     * @see Builder#setSsids(Set)
+     */
+    @NonNull
+    public Set<String> getSsids() {
+        return Collections.unmodifiableSet(mSsids);
     }
 
     /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
-    public static class Builder extends VcnUnderlyingNetworkTemplate.Builder<Builder> {
-        @Nullable private String mSsid;
+    public static final class Builder {
+        private int mNetworkQuality = NETWORK_QUALITY_ANY;
+        private int mMeteredMatchCriteria = MATCH_ANY;
+        @NonNull private final Set<String> mSsids = new ArraySet<>();
 
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * Set the required SSID.
+         * Set the required network quality to match this template.
          *
-         * @param ssid the required SSID, or {@code null} if any SSID is acceptable.
+         * <p>Network quality is a aggregation of multiple signals that reflect the network link
+         * metrics. For example, the network validation bit (see {@link
+         * NetworkCapabilities#NET_CAPABILITY_VALIDATED}), estimated first hop transport bandwidth
+         * and signal strength.
+         *
+         * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
+         * @hide
          */
         @NonNull
-        public Builder setSsid(@Nullable String ssid) {
-            mSsid = ssid;
+        public Builder setNetworkQuality(@NetworkQuality int networkQuality) {
+            validateNetworkQuality(networkQuality);
+
+            mNetworkQuality = networkQuality;
+            return this;
+        }
+
+        /**
+         * Set the matching criteria for metered networks.
+         *
+         * <p>A template where setMetered(MATCH_REQUIRED) will only match metered networks (one
+         * without NET_CAPABILITY_NOT_METERED). A template where setMetered(MATCH_FORBIDDEN) will
+         * only match a network that is not metered (one with NET_CAPABILITY_NOT_METERED).
+         *
+         * @param matchCriteria the matching criteria for metered networks. Defaults to {@link
+         *     #MATCH_ANY}.
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
+         */
+        // The matching getter is defined in the super class. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMetered()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setMetered(@MatchCriteria int matchCriteria) {
+            validateMatchCriteria(matchCriteria, "setMetered");
+
+            mMeteredMatchCriteria = matchCriteria;
+            return this;
+        }
+
+        /**
+         * Set the SSIDs with which a network can match this priority rule.
+         *
+         * @param ssids the matching SSIDs. Network with one of the matching SSIDs can match this
+         *     priority rule. If the set is empty, any SSID will match. The default is an empty set.
+         */
+        @NonNull
+        public Builder setSsids(@NonNull Set<String> ssids) {
+            validateSsids(ssids);
+
+            mSsids.clear();
+            mSsids.addAll(ssids);
             return this;
         }
 
         /** Build the VcnWifiUnderlyingNetworkTemplate. */
         @NonNull
         public VcnWifiUnderlyingNetworkTemplate build() {
-            return new VcnWifiUnderlyingNetworkTemplate(mNetworkQuality, mAllowMetered, mSsid);
-        }
-
-        /** @hide */
-        @Override
-        Builder self() {
-            return this;
+            return new VcnWifiUnderlyingNetworkTemplate(
+                    mNetworkQuality, mMeteredMatchCriteria, mSsids);
         }
     }
 }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 92d652d..d4a338b 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -335,6 +335,12 @@
     public static final int USER_ACTIVITY_EVENT_FACE_DOWN = 5;
 
     /**
+     * User activity event type: There is a change in the device state.
+     * @hide
+     */
+    public static final int USER_ACTIVITY_EVENT_DEVICE_STATE = 6;
+
+    /**
      * User activity flag: If already dimmed, extend the dim timeout
      * but do not brighten.  This flag is useful for keeping the screen on
      * a little longer without causing a visible change such as when
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 5831573..8834725 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -32,32 +32,30 @@
 public final class VibrationAttributes implements Parcelable {
     private static final String TAG = "VibrationAttributes";
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @IntDef(prefix = { "USAGE_CLASS_" }, value = {
             USAGE_CLASS_UNKNOWN,
             USAGE_CLASS_ALARM,
             USAGE_CLASS_FEEDBACK,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface UsageClass{}
+    public @interface UsageClass {}
 
-    /**
-     * @hide
-     */
+    /** @hide */
     @IntDef(prefix = { "USAGE_" }, value = {
             USAGE_UNKNOWN,
+            USAGE_ACCESSIBILITY,
             USAGE_ALARM,
-            USAGE_RINGTONE,
-            USAGE_NOTIFICATION,
             USAGE_COMMUNICATION_REQUEST,
-            USAGE_TOUCH,
-            USAGE_PHYSICAL_EMULATION,
             USAGE_HARDWARE_FEEDBACK,
+            USAGE_MEDIA,
+            USAGE_NOTIFICATION,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_RINGTONE,
+            USAGE_TOUCH,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface Usage{}
+    public @interface Usage {}
 
     /**
      * Vibration usage filter value to match all usages.
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d71ead7..eba9ff1 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -23,11 +23,14 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
+import android.os.vibrator.VibrationConfig;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -49,6 +52,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_OFF = 0;
 
     /**
@@ -56,6 +60,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_LOW = 1;
 
     /**
@@ -63,6 +68,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_MEDIUM = 2;
 
     /**
@@ -70,6 +76,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int VIBRATION_INTENSITY_HIGH = 3;
 
     /**
@@ -115,16 +122,12 @@
     }
 
     private final String mPackageName;
-    // The default vibration intensity level for haptic feedback.
-    @VibrationIntensity
-    private int mDefaultHapticFeedbackIntensity;
-    // The default vibration intensity level for notifications.
-    @VibrationIntensity
-    private int mDefaultNotificationVibrationIntensity;
-    // The default vibration intensity level for ringtones.
-    @VibrationIntensity
-    private int mDefaultRingVibrationIntensity;
-    private float mHapticChannelMaxVibrationAmplitude;
+    @Nullable
+    private final Resources mResources;
+
+    // This is lazily loaded only for the few clients that need this (e. Settings app).
+    @Nullable
+    private volatile VibrationConfig mVibrationConfig;
 
     /**
      * @hide to prevent subclassing from outside of the framework
@@ -132,8 +135,7 @@
     @UnsupportedAppUsage
     public Vibrator() {
         mPackageName = ActivityThread.currentPackageName();
-        final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
-        loadVibrationConfig(ctx);
+        mResources = null;
     }
 
     /**
@@ -141,26 +143,7 @@
      */
     protected Vibrator(Context context) {
         mPackageName = context.getOpPackageName();
-        loadVibrationConfig(context);
-    }
-
-    private void loadVibrationConfig(Context context) {
-        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
-        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
-        mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
-                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
-        mHapticChannelMaxVibrationAmplitude = loadFloat(context,
-                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
-    }
-
-    private int loadDefaultIntensity(Context ctx, int resId) {
-        return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
-    }
-
-    private float loadFloat(Context ctx, int resId, float defaultValue) {
-        return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
+        mResources = context.getResources();
     }
 
     /**
@@ -172,31 +155,30 @@
         return VibratorInfo.EMPTY_VIBRATOR_INFO;
     }
 
-    /**
-     * Get the default vibration intensity for haptic feedback.
-     *
-     * @hide
-     */
-    public int getDefaultHapticFeedbackIntensity() {
-        return mDefaultHapticFeedbackIntensity;
+    /** Get the static vibrator configuration from config.xml. */
+    private VibrationConfig getConfig() {
+        if (mVibrationConfig == null) {
+            Resources resources = mResources;
+            if (resources == null) {
+                final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
+                resources = ctx != null ? ctx.getResources() : null;
+            }
+            // This might be constructed more than once, but it only loads static config data from a
+            // xml file, so it would be ok.
+            mVibrationConfig = new VibrationConfig(resources);
+        }
+        return mVibrationConfig;
     }
 
     /**
-     * Get the default vibration intensity for notifications.
+     * Get the default vibration intensity for given usage.
      *
      * @hide
      */
-    public int getDefaultNotificationVibrationIntensity() {
-        return mDefaultNotificationVibrationIntensity;
-    }
-
-    /**
-     * Get the default vibration intensity for ringtones.
-     *
-     * @hide
-     */
-    public int getDefaultRingVibrationIntensity() {
-        return mDefaultRingVibrationIntensity;
+    @TestApi
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        return getConfig().getDefaultVibrationIntensity(usage);
     }
 
     /**
@@ -280,10 +262,7 @@
      * @hide
      */
     public float getHapticChannelMaximumAmplitude() {
-        if (mHapticChannelMaxVibrationAmplitude <= 0) {
-            return Float.NaN;
-        }
-        return mHapticChannelMaxVibrationAmplitude;
+        return getConfig().getHapticChannelMaximumAmplitude();
     }
 
     /**
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
new file mode 100644
index 0000000..4a61472
--- /dev/null
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.vibrator;
+
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
+import static android.os.VibrationAttributes.USAGE_ALARM;
+import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
+import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.Vibrator;
+import android.os.Vibrator.VibrationIntensity;
+
+/**
+ * List of device-specific internal vibration configuration loaded from platform config.xml.
+ *
+ * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by
+ * hidden methods, made available to Settings, SysUI and other platform client code. They can also
+ * be individually exposed with the necessary permissions by the {@link Vibrator} service.
+ *
+ * @hide
+ */
+public class VibrationConfig {
+
+    // TODO(b/191150049): move these to vibrator static config file
+    private final float mHapticChannelMaxVibrationAmplitude;
+    private final int mRampStepDurationMs;
+    private final int mRampDownDurationMs;
+
+    @VibrationIntensity
+    private final int mDefaultAlarmVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultHapticFeedbackIntensity;
+    @VibrationIntensity
+    private final int mDefaultMediaVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultNotificationVibrationIntensity;
+    @VibrationIntensity
+    private final int mDefaultRingVibrationIntensity;
+
+    /** @hide */
+    public VibrationConfig(@Nullable Resources resources) {
+        mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
+                com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
+        mRampDownDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0);
+        mRampStepDurationMs = loadInteger(resources,
+                com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0);
+
+        mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
+        mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
+        mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultMediaVibrationIntensity);
+        mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
+        mDefaultRingVibrationIntensity = loadDefaultIntensity(resources,
+                com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+    }
+
+    @VibrationIntensity
+    private static int loadDefaultIntensity(@Nullable Resources res, int resId) {
+        int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
+        int value = loadInteger(res, resId, defaultIntensity);
+        if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) {
+            return defaultIntensity;
+        }
+        return value;
+    }
+
+    private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) {
+        return res != null ? res.getFloat(resId) : defaultValue;
+    }
+
+    private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
+        return res != null ? res.getInteger(resId) : defaultValue;
+    }
+
+    /**
+     * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+     *
+     * @return a positive value representing the maximum absolute value the device can play signals
+     * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+     */
+    public float getHapticChannelMaximumAmplitude() {
+        if (mHapticChannelMaxVibrationAmplitude <= 0) {
+            return Float.NaN;
+        }
+        return mHapticChannelMaxVibrationAmplitude;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator
+     * when a vibration is cancelled or finished at non-zero amplitude.
+     */
+    public int getRampDownDurationMs() {
+        if (mRampDownDurationMs < 0) {
+            return 0;
+        }
+        return mRampDownDurationMs;
+    }
+
+    /**
+     * The duration, in milliseconds, that should be applied to convert vibration effect's
+     * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on
+     * devices without PWLE support.
+     */
+    public int getRampStepDurationMs() {
+        if (mRampStepDurationMs < 0) {
+            return 0;
+        }
+        return mRampStepDurationMs;
+    }
+
+    /** Get the default vibration intensity for given usage. */
+    @VibrationIntensity
+    public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
+        switch (usage) {
+            case USAGE_ALARM:
+                return mDefaultAlarmVibrationIntensity;
+            case USAGE_NOTIFICATION:
+            case USAGE_COMMUNICATION_REQUEST:
+                return mDefaultNotificationVibrationIntensity;
+            case USAGE_RINGTONE:
+                return mDefaultRingVibrationIntensity;
+            case USAGE_TOUCH:
+            case USAGE_HARDWARE_FEEDBACK:
+            case USAGE_PHYSICAL_EMULATION:
+            case USAGE_ACCESSIBILITY:
+                return mDefaultHapticFeedbackIntensity;
+            case USAGE_MEDIA:
+            case USAGE_UNKNOWN:
+                // fall through
+            default:
+                return mDefaultMediaVibrationIntensity;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "VibrationConfig{"
+                + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
+                + ", mRampStepDurationMs=" + mRampStepDurationMs
+                + ", mRampDownDurationMs=" + mRampDownDurationMs
+                + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
+                + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
+                + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
+                + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
+                + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+                + "}";
+    }
+}
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 4a94c32..90b5e51 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -67,6 +67,8 @@
     void revokeRuntimePermission(String packageName, String permissionName, int userId,
             String reason);
 
+    void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
+
     boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId);
 
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 3ea50e9..4f22876 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1362,6 +1362,26 @@
         return false;
     }
 
+    /**
+     * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
+     * USED in CTS or local tests.
+     *
+     * @param packageName The package to be revoked
+     * @param userId The user for which to revoke
+     *
+     * @hide
+     */
+    @TestApi
+    public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName,
+            int userId) {
+        try {
+            mPermissionManager.revokePostNotificationPermissionWithoutKillForTest(packageName,
+                    userId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     /* @hide */
     private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
         final IActivityManager am = ActivityManager.getService();
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 9bdfd8e..9d0c8d8 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -240,7 +240,7 @@
         mTag = parcel.readString();
         mCreationTime = parcel.readLong();
         mCopies = parcel.readInt();
-        Parcelable[] parcelables = parcel.readParcelableArray(null);
+        Parcelable[] parcelables = parcel.readParcelableArray(null, PageRange.class);
         if (parcelables != null) {
             mPageRanges = new PageRange[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0c8afa3..72e2863 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4603,6 +4603,43 @@
         public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
 
         /**
+         * The intensity of alarm vibrations, if configurable.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String ALARM_VIBRATION_INTENSITY =
+                "alarm_vibration_intensity";
+
+        /**
+         * The intensity of media vibrations, if configurable.
+         *
+         * This includes any vibration that is part of media, such as music, movie, soundtrack,
+         * game or animations.
+         *
+         * Not all devices are capable of changing their vibration intensity; on these devices
+         * there will likely be no difference between the various vibration intensities except for
+         * intensity 0 (off) and the rest.
+         *
+         * <b>Values:</b><br/>
+         * 0 - Vibration is disabled<br/>
+         * 1 - Weak vibrations<br/>
+         * 2 - Medium vibrations<br/>
+         * 3 - Strong vibrations
+         * @hide
+         */
+        public static final String MEDIA_VIBRATION_INTENSITY =
+                "media_vibration_intensity";
+
+        /**
          * The intensity of notification vibrations, if configurable.
          *
          * Not all devices are capable of changing their vibration intensity; on these devices
@@ -4619,6 +4656,7 @@
         @Readable
         public static final String NOTIFICATION_VIBRATION_INTENSITY =
                 "notification_vibration_intensity";
+
         /**
          * The intensity of ringtone vibrations, if configurable.
          *
@@ -4670,7 +4708,6 @@
          * 3 - Strong vibrations
          * @hide
          */
-        @Readable
         public static final String HARDWARE_HAPTIC_FEEDBACK_INTENSITY =
                 "hardware_haptic_feedback_intensity";
 
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 50f9d8a..163d6ed 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -35,6 +35,7 @@
 public abstract class DreamOverlayService extends Service {
     private static final String TAG = "DreamOverlayService";
     private static final boolean DEBUG = false;
+    private boolean mShowComplications;
 
     private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
         @Override
@@ -53,6 +54,8 @@
     @Nullable
     @Override
     public final IBinder onBind(@NonNull Intent intent) {
+        mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
+                DreamService.DEFAULT_SHOW_COMPLICATIONS);
         return mDreamOverlay.asBinder();
     }
 
@@ -74,4 +77,11 @@
             Log.e(TAG, "Could not request exit:" + e);
         }
     }
+
+    /**
+     * Returns whether to show complications on the dream overlay.
+     */
+    public final boolean shouldShowComplications() {
+        return mShowComplications;
+    }
 }
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 4ae3d7d..133e384 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -189,6 +189,19 @@
      */
     public static final String DREAM_META_DATA = "android.service.dream";
 
+    /**
+     * Extra containing a boolean for whether to show complications on the overlay.
+     * @hide
+     */
+    public static final String EXTRA_SHOW_COMPLICATIONS =
+            "android.service.dreams.SHOW_COMPLICATIONS";
+
+    /**
+     * The default value for whether to show complications on the overlay.
+     * @hide
+     */
+    public static final boolean DEFAULT_SHOW_COMPLICATIONS = true;
+
     private final IDreamManager mDreamManager;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private IBinder mDreamToken;
diff --git a/core/java/android/service/games/GameService.java b/core/java/android/service/games/GameService.java
index b79c055..105c2aa 100644
--- a/core/java/android/service/games/GameService.java
+++ b/core/java/android/service/games/GameService.java
@@ -38,10 +38,23 @@
  * when a game session should begin. It is always kept running by the system.
  * Because of this it should be kept as lightweight as possible.
  *
- * Heavy weight operations (such as showing UI) should be implemented in the
+ * <p>Heavyweight operations (such as showing UI) should be implemented in the
  * associated {@link GameSessionService} when a game session is taking place. Its
  * implementation should run in a separate process from the {@link GameService}.
  *
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_GAME_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:
+ * <pre>
+ * &lt;service android:name=".GameService"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_GAME_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.service.games.GameService" />
+ *     &lt;/intent-filter>
+ * &lt;/service>
+ * </pre>
+ *
  * @hide
  */
 @SystemApi
@@ -90,7 +103,7 @@
 
     @Override
     @Nullable
-    public IBinder onBind(@Nullable Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         if (ACTION_GAME_SERVICE.equals(intent.getAction())) {
             return mInterface.asBinder();
         }
diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java
index a2c8870..c1a3eb5 100644
--- a/core/java/android/service/games/GameSessionService.java
+++ b/core/java/android/service/games/GameSessionService.java
@@ -73,7 +73,7 @@
 
     @Override
     @Nullable
-    public IBinder onBind(@Nullable Intent intent) {
+    public final IBinder onBind(@Nullable Intent intent) {
         if (intent == null) {
             return null;
         }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 07d5fc5..25e0eca 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1896,7 +1896,7 @@
 
         private Canvas mCanvas;
         private Bitmap mBitmap;
-        private boolean mEnabledHwBitmapsInSwMode;
+        private boolean mEnabledHwFeaturesInSwMode;
 
         @Override
         public Canvas getCanvas(View view, int width, int height) {
@@ -1913,7 +1913,7 @@
             if (mCanvas == null) {
                 mCanvas = new Canvas();
             }
-            mEnabledHwBitmapsInSwMode = mCanvas.isHwBitmapsInSwModeEnabled();
+            mEnabledHwFeaturesInSwMode = mCanvas.isHwFeaturesInSwModeEnabled();
             mCanvas.setBitmap(mBitmap);
             return mCanvas;
         }
@@ -1921,7 +1921,7 @@
         @Override
         public Bitmap createBitmap() {
             mCanvas.setBitmap(null);
-            mCanvas.setHwBitmapsInSwModeEnabled(mEnabledHwBitmapsInSwMode);
+            mCanvas.setHwFeaturesInSwModeEnabled(mEnabledHwFeaturesInSwMode);
             return mBitmap;
         }
     }
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 36e779a..540f5dc 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -84,6 +84,12 @@
      */
     public static final int TYPE_SPLIT_SCREEN_DIVIDER = 5;
 
+    /**
+     * Window type: A system window used to show the UI for the interaction with
+     * window-based magnification, which includes the magnified content and the option menu.
+     */
+    public static final int TYPE_MAGNIFICATION_OVERLAY = 6;
+
     /* Special values for window IDs */
     /** @hide */
     public static final int ACTIVE_WINDOW_ID = Integer.MAX_VALUE;
@@ -801,6 +807,9 @@
             case TYPE_SPLIT_SCREEN_DIVIDER: {
                 return "TYPE_SPLIT_SCREEN_DIVIDER";
             }
+            case TYPE_MAGNIFICATION_OVERLAY: {
+                return "TYPE_MAGNIFICATION_OVERLAY";
+            }
             default:
                 return "<UNKNOWN:" + type + ">";
         }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6d58ee2..b21d08c 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -6628,6 +6628,7 @@
                 opts = ActivityOptions.makeBasic();
                 opts.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             }
+            opts.setLaunchDisplayId(view.getDisplay().getDisplayId());
             return Pair.create(intent, opts);
         }
     }
diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java
index 974a1dd..88ece5c 100644
--- a/core/java/android/window/DisplayAreaOrganizer.java
+++ b/core/java/android/window/DisplayAreaOrganizer.java
@@ -101,14 +101,6 @@
     public static final int FEATURE_IME_PLACEHOLDER = FEATURE_SYSTEM_FIRST + 7;
 
     /**
-     * Display area for one handed background layer, which preventing when user
-     * turning the Dark theme on, they can not clearly identify the screen has entered
-     * one handed mode.
-     * @hide
-     */
-    public static final int FEATURE_ONE_HANDED_BACKGROUND_PANEL = FEATURE_SYSTEM_FIRST + 8;
-
-    /**
      * Display area hosting IME window tokens (@see ImeContainer). By default, IMEs are parented
      * to FEATURE_IME_PLACEHOLDER but can be reparented under other RootDisplayArea.
      *
@@ -118,7 +110,7 @@
      * app on another screen).
      * @hide
      */
-    public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 9;
+    public static final int FEATURE_IME = FEATURE_SYSTEM_FIRST + 8;
 
     /**
      * The last boundary of display area for system features
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 733c0af..0130ef4 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -190,7 +190,7 @@
         mResInstanceState = source.readInt();
         mPresentityUri = source.readString();
         Parcelable[] tempParcelableArray = source.readParcelableArray(
-                                    PresTupleInfo.class.getClassLoader());
+                                    PresTupleInfo.class.getClassLoader(), PresTupleInfo.class);
         mTupleInfoArray = new PresTupleInfo[] {};
         if(tempParcelableArray != null) {
             mTupleInfoArray = Arrays.copyOf(tempParcelableArray, tempParcelableArray.length,
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 025f711..be7388b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -759,11 +759,11 @@
         }
 
         try {
-            IBinder permissionToken = ActivityTaskManager.getService()
-                    .requestStartActivityPermissionToken(getActivityToken());
             Intent delegationIntent = new Intent();
             final ComponentName delegateActivity = ComponentName.unflattenFromString(
                     Resources.getSystem().getString(R.string.config_chooserActivity));
+            IBinder permissionToken = ActivityTaskManager.getService()
+                    .requestStartActivityPermissionToken(delegateActivity);
             delegationIntent.setComponent(delegateActivity);
             delegationIntent.putExtra(Intent.EXTRA_INTENT, getIntent());
             delegationIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index b273f6d..f9a8c7b 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1437,11 +1437,11 @@
         try {
             // TODO: Once this is a small springboard activity, it can move off the UI process
             // and we can move the request method to ActivityManagerInternal.
-            IBinder permissionToken = ActivityTaskManager.getService()
-                    .requestStartActivityPermissionToken(getActivityToken());
             final Intent chooserIntent = new Intent();
             final ComponentName delegateActivity = ComponentName.unflattenFromString(
                     Resources.getSystem().getString(R.string.config_chooserActivity));
+            IBinder permissionToken = ActivityTaskManager.getService()
+                    .requestStartActivityPermissionToken(delegateActivity);
             chooserIntent.setClassName(delegateActivity.getPackageName(),
                     delegateActivity.getClassName());
             chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken);
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 289daee..301de2d 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -239,7 +239,8 @@
         mExtendedInfo = in.readCharSequence();
         mResolvedIntent = in.readParcelable(null /* ClassLoader */, android.content.Intent.class);
         mSourceIntents.addAll(
-                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */)));
+                Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */,
+                        Intent.class)));
         mIsSuspended = in.readBoolean();
         mPinned = in.readBoolean();
         mResolveInfo = in.readParcelable(null /* ClassLoader */, android.content.pm.ResolveInfo.class);
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 84391c1..0443ad0 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -24,7 +24,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.util.EventLog;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -585,6 +584,7 @@
     /**
      * @see #writeThrowable
      */
+    @SuppressWarnings("UnsafeParcelApi")
     private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
         final boolean hasThrowable = parcel.readBoolean();
         if (!hasThrowable) {
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2a203ac..b579be0 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -34,8 +34,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 
-import java.net.Inet4Address;
-import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -93,8 +91,8 @@
     public String interfaze;
     public String session;
     public int mtu = -1;
-    public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
-    public List<RouteInfo> routes = new ArrayList<RouteInfo>();
+    public List<LinkAddress> addresses = new ArrayList<>();
+    public List<RouteInfo> routes = new ArrayList<>();
     public List<String> dnsServers;
     public List<String> searchDomains;
     public List<String> allowedApplications;
@@ -114,12 +112,32 @@
     public VpnConfig() {
     }
 
-    public void updateAllowedFamilies(InetAddress address) {
-        if (address instanceof Inet4Address) {
-            allowIPv4 = true;
-        } else {
-            allowIPv6 = true;
-        }
+    public VpnConfig(VpnConfig other) {
+        user = other.user;
+        interfaze = other.interfaze;
+        session = other.session;
+        mtu = other.mtu;
+        addresses = copyOf(other.addresses);
+        routes = copyOf(other.routes);
+        dnsServers = copyOf(other.dnsServers);
+        searchDomains = copyOf(other.searchDomains);
+        allowedApplications = copyOf(other.allowedApplications);
+        disallowedApplications = copyOf(other.disallowedApplications);
+        configureIntent = other.configureIntent;
+        startTime = other.startTime;
+        legacy = other.legacy;
+        blocking = other.blocking;
+        allowBypass = other.allowBypass;
+        allowIPv4 = other.allowIPv4;
+        allowIPv6 = other.allowIPv6;
+        isMetered = other.isMetered;
+        underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
+                other.underlyingNetworks, other.underlyingNetworks.length) : null;
+        proxyInfo = other.proxyInfo;
+    }
+
+    private static <T> List<T> copyOf(List<T> list) {
+        return list != null ? new ArrayList<>(list) : null;
     }
 
     public void addLegacyRoutes(String routesStr) {
@@ -131,7 +149,6 @@
             //each route is ip/prefix
             RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST);
             this.routes.add(info);
-            updateAllowedFamilies(info.getDestination().getAddress());
         }
     }
 
@@ -144,7 +161,6 @@
             //each address is ip/prefix
             LinkAddress addr = new LinkAddress(address);
             this.addresses.add(addr);
-            updateAllowedFamilies(addr.getAddress());
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 21f719c..9429c79 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -26,6 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -37,7 +38,6 @@
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
 import android.location.GnssSignalQuality;
-import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
@@ -137,7 +137,9 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Queue;
+import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
@@ -12534,19 +12536,11 @@
     private NetworkStats mLastModemNetworkStats = new NetworkStats(0, -1);
 
     @VisibleForTesting
-    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
-        try {
-            if (!ArrayUtils.isEmpty(ifaces)) {
-                INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
-                        ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
-                if (statsService != null) {
-                    return statsService.getDetailedUidStats(ifaces);
-                } else {
-                    Slog.e(TAG, "Failed to get networkStatsService ");
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "failed to read network stats for ifaces: " + Arrays.toString(ifaces) + e);
+    protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+            String[] ifaces) {
+        Objects.requireNonNull(networkStatsManager);
+        if (!ArrayUtils.isEmpty(ifaces)) {
+            return networkStatsManager.getDetailedUidStats(Set.of(ifaces));
         }
         return null;
     }
@@ -12557,7 +12551,8 @@
      */
     @GuardedBy("this")
     public void updateWifiState(@Nullable final WifiActivityEnergyInfo info,
-            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+            @NonNull NetworkStatsManager networkStatsManager) {
         if (DEBUG_ENERGY) {
             synchronized (mWifiNetworkLock) {
                 Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
@@ -12567,7 +12562,8 @@
         // Grab a separate lock to acquire the network stats, which may do I/O.
         NetworkStats delta = null;
         synchronized (mWifiNetworkLock) {
-            final NetworkStats latestStats = readNetworkStatsLocked(mWifiIfaces);
+            final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+                    mWifiIfaces);
             if (latestStats != null) {
                 delta = NetworkStats.subtract(latestStats, mLastWifiNetworkStats, null, null,
                         mNetworkStatsPool.acquire());
@@ -12920,7 +12916,8 @@
      * Distribute Cell radio energy info and network traffic to apps.
      */
     public void noteModemControllerActivity(@Nullable final ModemActivityInfo activityInfo,
-            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs) {
+            final long consumedChargeUC, long elapsedRealtimeMs, long uptimeMs,
+            @NonNull NetworkStatsManager networkStatsManager) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
         }
@@ -12934,7 +12931,8 @@
         // Grab a separate lock to acquire the network stats, which may do I/O.
         NetworkStats delta = null;
         synchronized (mModemNetworkLock) {
-            final NetworkStats latestStats = readNetworkStatsLocked(mModemIfaces);
+            final NetworkStats latestStats = readNetworkStatsLocked(networkStatsManager,
+                    mModemIfaces);
             if (latestStats != null) {
                 delta = NetworkStats.subtract(latestStats, mLastModemNetworkStats, null, null,
                         mNetworkStatsPool.acquire());
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 627381c..09ff4e0 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -37,6 +37,7 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
+import android.view.RoundedCorner;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -229,13 +230,29 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (insets.getDisplayCutout() != null) {
-            mHeaderPaddingTop = insets.getDisplayCutout().getSafeInsetTop();
-            mWaterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
-        } else {
-            mHeaderPaddingTop = 0;
-            mWaterfallInsets = Insets.NONE;
+        int headerPaddingTop = 0;
+        Insets waterfallInsets = Insets.NONE;
+
+        final RoundedCorner topLeftRounded =
+                insets.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT);
+        if (topLeftRounded != null) {
+            headerPaddingTop = topLeftRounded.getRadius();
         }
+
+        final RoundedCorner topRightRounded =
+                insets.getRoundedCorner(RoundedCorner.POSITION_TOP_RIGHT);
+        if (topRightRounded != null) {
+            headerPaddingTop = Math.max(headerPaddingTop, topRightRounded.getRadius());
+        }
+
+        if (insets.getDisplayCutout() != null) {
+            headerPaddingTop =
+                    Math.max(headerPaddingTop, insets.getDisplayCutout().getSafeInsetTop());
+            waterfallInsets = insets.getDisplayCutout().getWaterfallInsets();
+        }
+
+        mHeaderPaddingTop = headerPaddingTop;
+        mWaterfallInsets = waterfallInsets;
         return super.onApplyWindowInsets(insets);
     }
 
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 73d6a17..e56d55e 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -212,6 +212,12 @@
         // DatabaseHelper.
         optional SettingProto in_silent = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto when_ringing = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+        optional SettingProto alarm_intensity = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto media_intensity = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto ring_intensity = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // notification_intensity is already logged at Notification.vibration_intensity
+        // haptic_feedback_intensity is already logged at HapticFeedback.intensity
     }
     optional Vibrate vibrate = 32;
 
diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto
index 7b97524d..fbe2170 100644
--- a/core/proto/android/server/vibrator/vibratormanagerservice.proto
+++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto
@@ -97,7 +97,7 @@
     optional int32 status = 6;
 }
 
-// Next id: 18
+// Next id: 24
 message VibratorManagerServiceDumpProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
     repeated int32 vibrator_ids = 1;
@@ -106,8 +106,14 @@
     optional VibrationProto current_external_vibration = 4;
     optional bool vibrator_under_external_control = 5;
     optional bool low_power_mode = 6;
+    optional int32 alarm_intensity = 18;
+    optional int32 alarm_default_intensity = 19;
     optional int32 haptic_feedback_intensity = 7;
     optional int32 haptic_feedback_default_intensity = 8;
+    optional int32 hardware_feedback_intensity = 22;
+    optional int32 hardware_feedback_default_intensity = 23;
+    optional int32 media_intensity = 20;
+    optional int32 media_default_intensity = 21;
     optional int32 notification_intensity = 9;
     optional int32 notification_default_intensity = 10;
     optional int32 ring_intensity = 11;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dfcfd5f..ae5414d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4438,6 +4438,12 @@
     <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
          android:protectionLevel="signature|installer|verifier" />
 
+   <!-- @TestApi Allows an application to revoke the POST_NOTIFICATIONS permission from an app
+        without killing the app. Only granted to the shell.
+        @hide -->
+    <permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows the system to read runtime permission state.
         @hide -->
     <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"
@@ -6094,7 +6100,7 @@
          @hide -->
     <permission android:name="android.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES"
                 android:protectionLevel="signature|role" />
-    
+
     <!-- @SystemApi Allows an app to read whether SafetyCenter is enabled/disabled.
              <p>Protection level: signature|privileged
              @hide
@@ -6102,6 +6108,14 @@
     <permission android:name="android.permission.READ_SAFETY_CENTER_STATUS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Required to access the safety center internal APIs using the
+             {@link android.safetycenter.SafetyCenterManager}.
+             <p>Protection level: internal|installer|role
+             @hide
+        -->
+    <permission android:name="android.permission.MANAGE_SAFETY_CENTER"
+                android:protectionLevel="internal|installer|role" />
+
     <!-- Attribution for Geofencing service. -->
     <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
     <!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 1705371..86c83c5 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8442,6 +8442,9 @@
         <attr name="settingsActivity" />
         <!-- A preview, in a drawable resource id, of what the Dream will look like. -->
         <attr name="previewImage" format="reference" />
+        <!-- Whether to show clock and other complications such as weather in the overlay. Default
+             to true. Note that the overlay on dreams is currently only supported on tablets. -->
+        <attr name="showClockAndComplications" format="boolean" />
     </declare-styleable>
 
     <!--  Use <code>trust-agent</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7d8bcea..2836c98 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1179,10 +1179,18 @@
     <string-array translatable="false" name="config_ringtoneEffectUris">
     </string-array>
 
+    <!-- The default intensity level for alarm vibrations. See
+         Settings.System.ALARM_VIBRATION_INTENSITY more details on the constant values and
+         meanings. -->
+    <integer name="config_defaultAlarmVibrationIntensity">2</integer>
     <!-- The default intensity level for haptic feedback. See
          Settings.System.HAPTIC_FEEDBACK_INTENSITY more details on the constant values and
          meanings. -->
     <integer name="config_defaultHapticFeedbackIntensity">2</integer>
+    <!-- The default intensity level for media vibrations. See
+         Settings.System.MEDIA_VIBRATION_INTENSITY more details on the constant values and
+         meanings. -->
+    <integer name="config_defaultMediaVibrationIntensity">2</integer>
     <!-- The default intensity level for notification vibrations. See
          Settings.System.NOTIFICATION_VIBRATION_INTENSITY more details on the constant values and
          meanings. -->
@@ -2125,6 +2133,8 @@
     <string name="config_deviceManager" translatable="false"></string>
     <!-- The name of the package that will hold the app protection service role. -->
     <string name="config_systemAppProtectionService" translatable="false"></string>
+    <!-- The name of the package that will hold the system calendar sync manager role. -->
+    <string name="config_systemAutomotiveCalendarSyncManager" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9449f60..5fa7409 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3250,6 +3250,7 @@
     <public name="supportedTypes" />
     <public name="resetEnabledSettingsOnAppDataCleared" />
     <public name="supportsStylusHandwriting" />
+    <public name="showClockAndComplications" />
     <!-- @hide @SystemApi -->
     <public name="gameSessionService" />
   </staging-public-group>
@@ -3273,6 +3274,8 @@
     <public name="config_deviceManager" />
     <!-- @hide @SystemApi -->
     <public name="config_systemAppProtectionService" />
+    <!-- @hide @SystemApi @TestApi -->
+    <public name="config_systemAutomotiveCalendarSyncManager" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ba4aa81..a4b5d3c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3867,7 +3867,9 @@
   <java-symbol type="drawable" name="ic_arrow_forward" />
   <java-symbol type="drawable" name="ic_permission" />
 
+  <java-symbol type="integer" name="config_defaultAlarmVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultHapticFeedbackIntensity" />
+  <java-symbol type="integer" name="config_defaultMediaVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultNotificationVibrationIntensity" />
   <java-symbol type="integer" name="config_defaultRingVibrationIntensity" />
 
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
index 47b14bb..4f8b855 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationBoundResourceCacheTest.java
@@ -16,6 +16,7 @@
 
 package android.content.res;
 
+import android.platform.test.annotations.Presubmit;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.TypedValue;
 
@@ -25,6 +26,7 @@
 
 import java.lang.reflect.InvocationTargetException;
 
+@Presubmit
 public class ConfigurationBoundResourceCacheTest
         extends ActivityInstrumentationTestCase2<ResourceCacheActivity> {
 
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 57f01e9..9aef2ca 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertNotNull;
 
 import android.app.Instrumentation;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -45,6 +46,7 @@
 /**
  * Tests for {@link FontResourcesParser}.
  */
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class FontResourcesParserTest {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
index c4df88b..f7f9569 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesDrawableTest.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.ColorStateListDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -34,6 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ResourcesDrawableTest {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
index aa1a534..25c3db5 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesLocaleTest.java
@@ -18,6 +18,7 @@
 
 import android.os.FileUtils;
 import android.os.LocaleList;
+import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
 import android.util.DisplayMetrics;
 
@@ -30,6 +31,7 @@
 import java.util.Arrays;
 import java.util.Locale;
 
+@Presubmit
 public class ResourcesLocaleTest extends AndroidTestCase {
 
     private String extractApkAndGetPath(int id) throws Exception {
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index e7ee9dc..34a8bde 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -20,6 +20,7 @@
 import android.app.ResourcesManager;
 import android.os.Binder;
 import android.os.LocaleList;
+import android.platform.test.annotations.Postsubmit;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.Display;
@@ -32,6 +33,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+@Postsubmit
 public class ResourcesManagerTest extends TestCase {
     private static final int SECONDARY_DISPLAY_ID = 1;
     private static final String APP_ONE_RES_DIR = "app_one.apk";
diff --git a/core/tests/coretests/src/android/content/res/TEST_MAPPING b/core/tests/coretests/src/android/content/res/TEST_MAPPING
new file mode 100644
index 0000000..4ea6e40
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/TEST_MAPPING
@@ -0,0 +1,43 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.content.res."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.content.res."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Postsubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index bdd76a5..981086d 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -27,14 +27,22 @@
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
 import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -50,11 +58,19 @@
 @RunWith(MockitoJUnitRunner.class)
 public class VibratorTest {
 
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
+
+    private Context mContextSpy;
     private Vibrator mVibratorSpy;
 
     @Before
     public void setUp() {
-        mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class));
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+
+        ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
+        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+        mVibratorSpy = spy(new SystemVibrator(mContextSpy));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
new file mode 100644
index 0000000..9aed8be
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/res/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.content."
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 8d9d79d..ce2f764 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
@@ -44,6 +45,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -51,6 +53,8 @@
 public class MobileRadioPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+    @Mock
+    NetworkStatsManager mNetworkStatsManager;
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -95,7 +99,8 @@
 
         ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
                 new int[]{100, 200, 300, 400, 500}, 600);
-        stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000);
+        stats.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE, 10000, 10000,
+                mNetworkStatsManager);
 
         mStatsRule.setTime(12_000_000, 12_000_000);
 
@@ -157,7 +162,8 @@
         mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
 
-        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000);
+        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000,
+                mNetworkStatsManager);
 
         uid.setProcessStateForTest(
                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
@@ -165,7 +171,8 @@
         mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
 
-        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000);
+        stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
+                mNetworkStatsManager);
 
         assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
         // 12000-8000 = 4000 ms == 4_000_000 us
@@ -239,7 +246,7 @@
 
         ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
                 new int[]{100, 200, 300, 400, 500}, 600);
-        stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000);
+        stats.noteModemControllerActivity(mai, 10_000_000, 10000, 10000, mNetworkStatsManager);
 
         mStatsRule.setTime(12_000_000, 12_000_000);
 
@@ -301,7 +308,7 @@
         mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
 
-        stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000);
+        stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager);
 
         uid.setProcessStateForTest(
                 BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
@@ -309,7 +316,7 @@
         mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
                 .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
 
-        stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000);
+        stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager);
 
         mStatsRule.setTime(20000, 20000);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 4faf349..bddb3a1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -20,6 +20,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkStats;
 import android.os.Handler;
 import android.os.Looper;
@@ -116,7 +118,8 @@
     }
 
     @Override
-    protected NetworkStats readNetworkStatsLocked(String[] ifaces) {
+    protected NetworkStats readNetworkStatsLocked(@NonNull NetworkStatsManager networkStatsManager,
+            String[] ifaces) {
         return mNetworkStats;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index fc44ddc..e7ce9a0 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -21,6 +21,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
@@ -35,6 +36,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -43,6 +45,9 @@
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
+    @Mock
+    NetworkStatsManager mNetworkStatsManager;
+
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
             .setAveragePower(PowerProfile.POWER_WIFI_CONTROLLER_IDLE, 360.0)
@@ -80,7 +85,8 @@
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000);
+        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -113,7 +119,7 @@
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000);
+        batteryStats.updateWifiState(energyInfo, 1_000_000, 1000, 1000, mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(calculator);
@@ -160,7 +166,8 @@
 
         // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
         // on the packet counts.
-        batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000);
+        batteryStats.updateWifiState(/* energyInfo */ null, POWER_DATA_UNAVAILABLE, 1000, 1000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
@@ -180,7 +187,8 @@
 
         // Don't pass WifiActivityEnergyInfo, making WifiPowerCalculator rely exclusively
         // on the packet counts.
-        batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000);
+        batteryStats.updateWifiState(/* energyInfo */ null, 1_000_000, 1000, 1000,
+                mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
         mStatsRule.apply(calculator);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 13303a3..878d302 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -293,6 +293,7 @@
 
     <privapp-permissions package="com.android.shell">
         <!-- Needed for test only -->
+        <permission name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP"/>
         <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
         <permission name="android.permission.ACCESS_LOWPAN_STATE"/>
         <permission name="android.permission.BACKUP"/>
@@ -518,9 +519,8 @@
         <permission name="android.permission.MANAGE_VOICE_KEYPHRASES" />
         <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
         <permission name="android.permission.LOCK_DEVICE" />
-        <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+        <!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
-        <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
     </privapp-permissions>
 
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a612265..425a378 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -67,7 +67,7 @@
      * @hide
      */
     protected int mDensity = Bitmap.DENSITY_NONE;
-    private boolean mAllowHwBitmapsInSwMode = false;
+    private boolean mAllowHwFeaturesInSwMode = false;
 
     protected void throwIfCannotDraw(Bitmap bitmap) {
         if (bitmap.isRecycled()) {
@@ -101,14 +101,14 @@
 
     public void drawArc(float left, float top, float right, float bottom, float startAngle,
             float sweepAngle, boolean useCenter, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
                 useCenter, paint.getNativeInstance());
     }
 
     public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,
                 paint);
     }
@@ -119,14 +119,14 @@
 
     public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawBitmap(mNativeCanvasWrapper, bitmap.getNativeInstance(), left, top,
                 paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity,
                 bitmap.mDensity);
     }
 
     public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.getNativeInstance(), matrix.ni(),
                 paint != null ? paint.getNativeInstance() : 0);
     }
@@ -137,7 +137,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         int left, top, right, bottom;
@@ -163,7 +163,7 @@
             throw new NullPointerException();
         }
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
 
         float left, top, right, bottom;
@@ -202,7 +202,7 @@
                 || (lastScanline + width > length)) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         // quick escape if there's nothing to draw
         if (width == 0 || height == 0) {
             return;
@@ -226,7 +226,7 @@
         if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (meshWidth == 0 || meshHeight == 0) {
             return;
         }
@@ -243,7 +243,7 @@
     }
 
     public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
     }
 
@@ -275,23 +275,23 @@
 
     public void drawLine(float startX, float startY, float stopX, float stopY,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, int offset, int count,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawLines(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawLines(pts, 0, pts.length, paint);
     }
 
     public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawOval(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
@@ -299,18 +299,19 @@
         if (oval == null) {
             throw new NullPointerException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawOval(oval.left, oval.top, oval.right, oval.bottom, paint);
     }
 
     public void drawPaint(@NonNull Paint paint) {
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPaint(mNativeCanvasWrapper, paint.getNativeInstance());
     }
 
     public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -320,7 +321,7 @@
     public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
         Bitmap bitmap = patch.getBitmap();
         throwIfCannotDraw(bitmap);
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
         nDrawNinePatch(mNativeCanvasWrapper, bitmap.getNativeInstance(), patch.mNativeChunk,
                 dst.left, dst.top, dst.right, dst.bottom, nativePaint,
@@ -328,7 +329,7 @@
     }
 
     public void drawPath(@NonNull Path path, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (path.isSimplePath && path.rects != null) {
             nDrawRegion(mNativeCanvasWrapper, path.rects.mNativeRegion, paint.getNativeInstance());
         } else {
@@ -337,18 +338,18 @@
     }
 
     public void drawPoint(float x, float y, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPoint(mNativeCanvasWrapper, x, y, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) float[] pts, int offset, int count,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawPoints(mNativeCanvasWrapper, pts, offset, count, paint.getNativeInstance());
     }
 
     public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawPoints(pts, 0, pts.length, paint);
     }
 
@@ -359,7 +360,7 @@
         if (index < 0 || index + count > text.length || count * 2 > pos.length) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         for (int i = 0; i < count; i++) {
             drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
         }
@@ -368,22 +369,22 @@
     @Deprecated
     public void drawPosText(@NonNull String text, @NonNull @Size(multiple = 2) float[] pos,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
     }
 
     public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.getNativeInstance());
     }
 
     public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawRect(r.left, r.top, r.right, r.bottom, paint);
     }
 
     public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRect(mNativeCanvasWrapper,
                 rect.left, rect.top, rect.right, rect.bottom, paint.getNativeInstance());
     }
@@ -394,13 +395,13 @@
 
     public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
             @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry,
                 paint.getNativeInstance());
     }
 
     public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
     }
 
@@ -410,7 +411,7 @@
      */
     public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
             @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         float outerLeft = outer.left;
         float outerTop = outer.top;
         float outerRight = outer.right;
@@ -431,7 +432,7 @@
      */
     public void drawDoubleRoundRect(@NonNull RectF outer, @NonNull float[] outerRadii,
             @NonNull RectF inner, @NonNull float[] innerRadii, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (innerRadii == null || outerRadii == null
                 || innerRadii.length != 8 || outerRadii.length != 8) {
             throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
@@ -509,7 +510,7 @@
                 (text.length - index - count)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, index, count, x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -519,7 +520,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -537,7 +538,7 @@
     }
 
     public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -547,7 +548,7 @@
         if ((start | end | (end - start) | (text.length() - end)) < 0) {
             throw new IndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawText(mNativeCanvasWrapper, text, start, end, x, y, paint.mBidiFlags,
                 paint.getNativeInstance());
     }
@@ -557,7 +558,7 @@
         if (index < 0 || index + count > text.length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.readOnlyNI(), hOffset, vOffset,
                 paint.mBidiFlags, paint.getNativeInstance());
@@ -566,7 +567,7 @@
     public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
             float vOffset, @NonNull Paint paint) {
         if (text.length() > 0) {
-            throwIfHasHwBitmapInSwMode(paint);
+            throwIfHasHwFeaturesInSwMode(paint);
             nDrawTextOnPath(mNativeCanvasWrapper, text, path.readOnlyNI(), hOffset, vOffset,
                     paint.mBidiFlags, paint.getNativeInstance());
         }
@@ -587,7 +588,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
                 x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */);
     }
@@ -606,7 +607,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         if (text instanceof String || text instanceof SpannedString ||
                 text instanceof SpannableString) {
             nDrawTextRun(mNativeCanvasWrapper, text.toString(), start, end, contextStart,
@@ -664,7 +665,7 @@
         if (indices != null) {
             checkRange(indices.length, indexOffset, indexCount);
         }
-        throwIfHasHwBitmapInSwMode(paint);
+        throwIfHasHwFeaturesInSwMode(paint);
         nDrawVertices(mNativeCanvasWrapper, mode.nativeInt, vertexCount, verts,
                 vertOffset, texs, texOffset, colors, colorOffset,
                 indices, indexOffset, indexCount, paint.getNativeInstance());
@@ -680,50 +681,52 @@
     /**
      * @hide
      */
-    public void setHwBitmapsInSwModeEnabled(boolean enabled) {
-        mAllowHwBitmapsInSwMode = enabled;
+    public void setHwFeaturesInSwModeEnabled(boolean enabled) {
+        mAllowHwFeaturesInSwMode = enabled;
     }
 
     /**
      * @hide
      */
-    public boolean isHwBitmapsInSwModeEnabled() {
-        return mAllowHwBitmapsInSwMode;
+    public boolean isHwFeaturesInSwModeEnabled() {
+        return mAllowHwFeaturesInSwMode;
     }
 
     /**
+     * If true throw an exception
      * @hide
      */
-    protected void onHwBitmapInSwMode() {
-        if (!mAllowHwBitmapsInSwMode) {
+    protected boolean onHwFeatureInSwMode() {
+        return !mAllowHwFeaturesInSwMode;
+    }
+
+    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
+        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE
+                && onHwFeatureInSwMode()) {
             throw new IllegalArgumentException(
                     "Software rendering doesn't support hardware bitmaps");
         }
     }
 
-    private void throwIfHwBitmapInSwMode(Bitmap bitmap) {
-        if (!isHardwareAccelerated() && bitmap.getConfig() == Bitmap.Config.HARDWARE) {
-            onHwBitmapInSwMode();
-        }
-    }
-
-    private void throwIfHasHwBitmapInSwMode(Paint p) {
+    private void throwIfHasHwFeaturesInSwMode(Paint p) {
         if (isHardwareAccelerated() || p == null) {
             return;
         }
-        throwIfHasHwBitmapInSwMode(p.getShader());
+        throwIfHasHwFeaturesInSwMode(p.getShader());
     }
 
-    private void throwIfHasHwBitmapInSwMode(Shader shader) {
+    private void throwIfHasHwFeaturesInSwMode(Shader shader) {
         if (shader == null) {
             return;
         }
         if (shader instanceof BitmapShader) {
             throwIfHwBitmapInSwMode(((BitmapShader) shader).mBitmap);
-        }
-        if (shader instanceof ComposeShader) {
-            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderA);
-            throwIfHasHwBitmapInSwMode(((ComposeShader) shader).mShaderB);
+        } else if (shader instanceof RuntimeShader && onHwFeatureInSwMode()) {
+            throw new IllegalArgumentException(
+                    "Software rendering doesn't support RuntimeShader");
+        } else if (shader instanceof ComposeShader) {
+            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderA);
+            throwIfHasHwFeaturesInSwMode(((ComposeShader) shader).mShaderB);
         }
     }
 
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index fc7f84c..618e6dc 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -169,8 +169,7 @@
     }
 
     /**
-     * Sets the Outline to the rounded rect defined by the input rect, and
-     * corner radius.
+     * Sets the Outline to the rect defined by the input coordinates.
      */
     public void setRect(int left, int top, int right, int bottom) {
         setRoundRect(left, top, right, bottom, 0.0f);
@@ -184,7 +183,7 @@
     }
 
     /**
-     * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+     * Sets the Outline to the rounded rect defined by the input coordinates and corner radius.
      * <p>
      * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
      */
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 390d3d4..ee4165b 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -124,7 +124,7 @@
     public void endRecording() {
         verifyValid();
         if (mRecordingCanvas != null) {
-            mRequiresHwAcceleration = mRecordingCanvas.mHoldsHwBitmap;
+            mRequiresHwAcceleration = mRecordingCanvas.mUsesHwFeature;
             mRecordingCanvas = null;
             nativeEndRecording(mNativePicture);
         }
@@ -182,8 +182,10 @@
         if (mRecordingCanvas != null) {
             endRecording();
         }
-        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()) {
-            canvas.onHwBitmapInSwMode();
+        if (mRequiresHwAcceleration && !canvas.isHardwareAccelerated()
+                && canvas.onHwFeatureInSwMode()) {
+            throw new IllegalArgumentException("Software rendering not supported for Pictures that"
+                    + " require hardware acceleration");
         }
         nativeDraw(canvas.getNativeCanvasWrapper(), mNativePicture);
     }
@@ -242,7 +244,7 @@
 
     private static class PictureCanvas extends Canvas {
         private final Picture mPicture;
-        boolean mHoldsHwBitmap;
+        boolean mUsesHwFeature;
 
         public PictureCanvas(Picture pict, long nativeCanvas) {
             super(nativeCanvas);
@@ -265,8 +267,9 @@
         }
 
         @Override
-        protected void onHwBitmapInSwMode() {
-            mHoldsHwBitmap = true;
+        protected boolean onHwFeatureInSwMode() {
+            mUsesHwFeature = true;
+            return false;
         }
     }
 }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b843589..ffaa4ea 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -868,7 +868,7 @@
     private void drawPatterned(@NonNull Canvas canvas) {
         final Rect bounds = mHotspotBounds;
         final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-        boolean useCanvasProps = shouldUseCanvasProps(canvas);
+        boolean useCanvasProps = !mForceSoftware;
         if (isBounded()) {
             canvas.clipRect(getDirtyBounds());
         }
@@ -914,7 +914,11 @@
         }
         for (int i = 0; i < mRunningAnimations.size(); i++) {
             RippleAnimationSession s = mRunningAnimations.get(i);
-            if (useCanvasProps) {
+            if (!canvas.isHardwareAccelerated()) {
+                Log.e(TAG, "The RippleDrawable.STYLE_PATTERNED animation is not supported for a "
+                        + "non-hardware accelerated Canvas. Skipping animation.");
+                break;
+            } else if (useCanvasProps) {
                 RippleAnimationSession.AnimationProperties<CanvasProperty<Float>,
                         CanvasProperty<Paint>>
                         p = s.getCanvasProperties();
@@ -1002,10 +1006,6 @@
         return color;
     }
 
-    private boolean shouldUseCanvasProps(Canvas c) {
-        return !mForceSoftware && c.isHardwareAccelerated();
-    }
-
     @Override
     public void invalidateSelf() {
         invalidateSelf(true);
diff --git a/libs/WindowManager/Shell/res/layout/background_panel.xml b/libs/WindowManager/Shell/res/layout/background_panel.xml
new file mode 100644
index 0000000..c3569d8
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/background_panel.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/background_panel_layout"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="center_horizontal | center_vertical"
+    android:background="@android:color/transparent">
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 9374da4..f878a46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -231,8 +231,9 @@
      * Fade animation for consecutive flyouts.
      */
     void animateUpdate(Bubble.FlyoutMessage flyoutMessage, PointF stackPos,
-            boolean hideDot, Runnable onHide) {
+            boolean hideDot, float[] dotCenter, Runnable onHide) {
         mOnHide = onHide;
+        mDotCenter = dotCenter;
         final Runnable afterFadeOut = () -> {
             updateFlyoutMessage(flyoutMessage);
             // Wait for TextViews to layout with updated height.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 7bf4439..d5d8b71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -2516,6 +2516,7 @@
             if (mFlyout.getVisibility() == View.VISIBLE) {
                 mFlyout.animateUpdate(bubble.getFlyoutMessage(),
                         mStackAnimationController.getStackPosition(), !bubble.showDot(),
+                        bubble.getIconView().getDotCenter(),
                         mAfterFlyoutHidden /* onHide */);
             } else {
                 mFlyout.setVisibility(INVISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index ad9ebb2..36e55ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -137,14 +137,16 @@
             return;
         }
 
-        if (mIcon == null) {
-            // TODO: add fade-in animation.
+        if (mBackgroundLeash == null) {
             mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
                     RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
             t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
                     .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1)
                     .show(mBackgroundLeash);
+        }
 
+        if (mIcon == null && resizingTask.topActivityInfo != null) {
+            // TODO: add fade-in animation.
             mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
             mResizingIconView.setImageDrawable(mIcon);
             mResizingIconView.setVisibility(View.VISIBLE);
@@ -168,12 +170,16 @@
             return;
         }
 
+        if (mBackgroundLeash != null) {
+            t.remove(mBackgroundLeash);
+            mBackgroundLeash = null;
+        }
+
         if (mIcon != null) {
             mResizingIconView.setVisibility(View.GONE);
             mResizingIconView.setImageDrawable(null);
-            t.remove(mBackgroundLeash).hide(mIconLeash);
+            t.hide(mIconLeash);
             mIcon = null;
-            mBackgroundLeash = null;
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
new file mode 100644
index 0000000..c20b7d9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.onehanded;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
+import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.IWindow;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Holds view hierarchy of a root surface and helps inflate a themeable view for background.
+ */
+public final class BackgroundWindowManager extends WindowlessWindowManager {
+    private static final String TAG = BackgroundWindowManager.class.getSimpleName();
+    private static final int THEME_COLOR_OFFSET = 10;
+
+    private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mTransactionFactory;
+
+    private Context mContext;
+    private Rect mDisplayBounds;
+    private SurfaceControlViewHost mViewHost;
+    private SurfaceControl mLeash;
+    private View mBackgroundView;
+    private @OneHandedState.State int mCurrentState;
+
+    public BackgroundWindowManager(Context context) {
+        super(context.getResources().getConfiguration(), null /* rootSurface */,
+                null /* hostInputToken */);
+        mContext = context;
+        mTransactionFactory = SurfaceControl.Transaction::new;
+    }
+
+    @Override
+    public SurfaceControl getSurfaceControl(IWindow window) {
+        return super.getSurfaceControl(window);
+    }
+
+    @Override
+    public void setConfiguration(Configuration configuration) {
+        super.setConfiguration(configuration);
+        mContext = mContext.createConfigurationContext(configuration);
+    }
+
+    /**
+     * onConfigurationChanged events for updating background theme color.
+     */
+    public void onConfigurationChanged() {
+        if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
+            updateThemeOnly();
+        }
+    }
+
+    /**
+     * One-handed mode state changed callback
+     * @param newState of One-handed mode representing by {@link OneHandedState}
+     */
+    public void onStateChanged(int newState) {
+        mCurrentState = newState;
+    }
+
+    @Override
+    protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+        final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+                .setColorLayer()
+                .setBufferSize(mDisplayBounds.width(), mDisplayBounds.height())
+                .setFormat(PixelFormat.RGB_888)
+                .setOpaque(true)
+                .setName(TAG)
+                .setCallsite("BackgroundWindowManager#attachToParentSurface");
+        mLeash = builder.build();
+        b.setParent(mLeash);
+    }
+
+    /** Inflates background view on to the root surface. */
+    boolean initView() {
+        if (mBackgroundView != null || mViewHost != null) {
+            return false;
+        }
+
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        mBackgroundView = (View) LayoutInflater.from(mContext)
+                .inflate(R.layout.background_panel, null /* root */);
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                mDisplayBounds.width(), mDisplayBounds.height(), 0 /* TYPE NONE */,
+                FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL | FLAG_WATCH_OUTSIDE_TOUCH
+                        | FLAG_SLIPPERY, PixelFormat.TRANSLUCENT);
+        lp.token = new Binder();
+        lp.setTitle("background-panel");
+        lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+        mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+        mViewHost.setView(mBackgroundView, lp);
+        return true;
+    }
+
+    /**
+     * Called when onDisplayAdded() or onDisplayRemoved() callback.
+     * @param displayLayout The latest {@link DisplayLayout} for display bounds.
+     */
+    public void onDisplayChanged(DisplayLayout displayLayout) {
+        // One-handed mode is only available on portrait.
+        if (displayLayout.height() > displayLayout.width()) {
+            mDisplayBounds = new Rect(0, 0, displayLayout.width(), displayLayout.height());
+        } else {
+            mDisplayBounds = new Rect(0, 0, displayLayout.height(), displayLayout.width());
+        }
+    }
+
+    private void updateThemeOnly() {
+        if (mBackgroundView == null || mViewHost == null || mLeash == null) {
+            Slog.w(TAG, "Background view or SurfaceControl does not exist when trying to "
+                    + "update theme only!");
+            return;
+        }
+
+        WindowManager.LayoutParams lp = (WindowManager.LayoutParams)
+                mBackgroundView.getLayoutParams();
+        mBackgroundView.setBackgroundColor(getThemeColorForBackground());
+        mViewHost.setView(mBackgroundView, lp);
+    }
+
+    /**
+     * Shows the background layer when One-handed mode triggered.
+     */
+    public void showBackgroundLayer() {
+        if (!initView()) {
+            updateThemeOnly();
+            return;
+        }
+        if (mLeash == null) {
+            Slog.w(TAG, "SurfaceControl mLeash is null, can't show One-handed mode "
+                    + "background panel!");
+            return;
+        }
+
+        mTransactionFactory.getTransaction()
+                .setAlpha(mLeash, 1.0f)
+                .setLayer(mLeash, -1 /* at bottom-most layer */)
+                .show(mLeash)
+                .apply();
+    }
+
+    /**
+     * Remove the leash of background layer after stop One-handed mode.
+     */
+    public void removeBackgroundLayer() {
+        if (mBackgroundView != null) {
+            mBackgroundView = null;
+        }
+
+        if (mViewHost != null) {
+            mViewHost.release();
+            mViewHost = null;
+        }
+
+        if (mLeash != null) {
+            mTransactionFactory.getTransaction().remove(mLeash).apply();
+            mLeash = null;
+        }
+    }
+
+    /**
+     * Gets {@link SurfaceControl} of the background layer.
+     * @return {@code null} if not exist.
+     */
+    @Nullable
+    SurfaceControl getSurfaceControl() {
+        return mLeash;
+    }
+
+    private int getThemeColor() {
+        final Context themedContext = new ContextThemeWrapper(mContext,
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        return themedContext.getColor(R.color.one_handed_tutorial_background_color);
+    }
+
+    int getThemeColorForBackground() {
+        final int origThemeColor = getThemeColor();
+        return android.graphics.Color.argb(Color.alpha(origThemeColor),
+                Color.red(origThemeColor) - THEME_COLOR_OFFSET,
+                Color.green(origThemeColor) - THEME_COLOR_OFFSET,
+                Color.blue(origThemeColor) - THEME_COLOR_OFFSET);
+    }
+
+    private float adjustColor(int origColor) {
+        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
+    }
+
+    void dump(@NonNull PrintWriter pw) {
+        final String innerPrefix = "  ";
+        pw.println(TAG);
+        pw.print(innerPrefix + "mDisplayBounds=");
+        pw.println(mDisplayBounds);
+        pw.print(innerPrefix + "mViewHost=");
+        pw.println(mViewHost);
+        pw.print(innerPrefix + "mLeash=");
+        pw.println(mLeash);
+        pw.print(innerPrefix + "mBackgroundView=");
+        pw.println(mBackgroundView);
+    }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
deleted file mode 100644
index 9e1c61a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.onehanded;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.view.ContextThemeWrapper;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.view.animation.LinearInterpolator;
-import android.window.DisplayAreaAppearedInfo;
-import android.window.DisplayAreaInfo;
-import android.window.DisplayAreaOrganizer;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
-
-import java.io.PrintWriter;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Manages OneHanded color background layer areas.
- * To avoid when turning the Dark theme on, users can not clearly identify
- * the screen has entered one handed mode.
- */
-public class OneHandedBackgroundPanelOrganizer extends DisplayAreaOrganizer
-        implements OneHandedAnimationCallback, OneHandedState.OnStateChangedListener {
-    private static final String TAG = "OneHandedBackgroundPanelOrganizer";
-    private static final int THEME_COLOR_OFFSET = 10;
-    private static final int ALPHA_ANIMATION_DURATION = 200;
-
-    private final Context mContext;
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
-            mTransactionFactory;
-
-    private @OneHandedState.State int mCurrentState;
-    private ValueAnimator mAlphaAnimator;
-
-    private float mTranslationFraction;
-    private float[] mThemeColor;
-
-    /**
-     * The background to distinguish the boundary of translated windows and empty region when
-     * one handed mode triggered.
-     */
-    private Rect mBkgBounds;
-    private Rect mStableInsets;
-
-    @Nullable
-    @VisibleForTesting
-    SurfaceControl mBackgroundSurface;
-    @Nullable
-    private SurfaceControl mParentLeash;
-
-    public OneHandedBackgroundPanelOrganizer(Context context, DisplayLayout displayLayout,
-            OneHandedSettingsUtil settingsUtil, Executor executor) {
-        super(executor);
-        mContext = context;
-        mTranslationFraction = settingsUtil.getTranslationFraction(context);
-        mTransactionFactory = SurfaceControl.Transaction::new;
-        updateThemeColors();
-    }
-
-    @Override
-    public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
-            @NonNull SurfaceControl leash) {
-        mParentLeash = leash;
-    }
-
-    @Override
-    public List<DisplayAreaAppearedInfo> registerOrganizer(int displayAreaFeature) {
-        final List<DisplayAreaAppearedInfo> displayAreaInfos;
-        displayAreaInfos = super.registerOrganizer(displayAreaFeature);
-        for (int i = 0; i < displayAreaInfos.size(); i++) {
-            final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
-            onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
-        }
-        return displayAreaInfos;
-    }
-
-    @Override
-    public void unregisterOrganizer() {
-        super.unregisterOrganizer();
-        removeBackgroundPanelLayer();
-        mParentLeash = null;
-    }
-
-    @Override
-    public void onAnimationUpdate(SurfaceControl.Transaction tx, float xPos, float yPos) {
-        final int yTopPos = (mStableInsets.top - mBkgBounds.height()) + Math.round(yPos);
-        tx.setPosition(mBackgroundSurface, 0, yTopPos);
-    }
-
-    @Nullable
-    @VisibleForTesting
-    boolean isRegistered() {
-        return mParentLeash != null;
-    }
-
-    void createBackgroundSurface() {
-        mBackgroundSurface = new SurfaceControl.Builder(mSurfaceSession)
-                .setBufferSize(mBkgBounds.width(), mBkgBounds.height())
-                .setColorLayer()
-                .setFormat(PixelFormat.RGB_888)
-                .setOpaque(true)
-                .setName("one-handed-background-panel")
-                .setCallsite("OneHandedBackgroundPanelOrganizer")
-                .build();
-
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        mAlphaAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
-        mAlphaAnimator.setInterpolator(new LinearInterpolator());
-        mAlphaAnimator.setDuration(ALPHA_ANIMATION_DURATION);
-        mAlphaAnimator.addUpdateListener(
-                animator -> detachBackgroundFromParent(animator));
-    }
-
-    void detachBackgroundFromParent(ValueAnimator animator) {
-        if (mBackgroundSurface == null || mParentLeash == null) {
-            return;
-        }
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        final float currentValue = (float) animator.getAnimatedValue();
-        final SurfaceControl.Transaction tx = mTransactionFactory.getTransaction();
-        if (currentValue == 0.0f) {
-            tx.reparent(mBackgroundSurface, null).apply();
-        } else {
-            tx.setAlpha(mBackgroundSurface, (float) animator.getAnimatedValue()).apply();
-        }
-    }
-
-    /**
-     * Called when onDisplayAdded() or onDisplayRemoved() callback.
-     *
-     * @param displayLayout The latest {@link DisplayLayout} representing current displayId
-     */
-    public void onDisplayChanged(DisplayLayout displayLayout) {
-        mStableInsets = displayLayout.stableInsets();
-        // Ensure the mBkgBounds is portrait, due to OHM only support on portrait
-        if (displayLayout.height() > displayLayout.width()) {
-            mBkgBounds = new Rect(0, 0, displayLayout.width(),
-                    Math.round(displayLayout.height() * mTranslationFraction) + mStableInsets.top);
-        } else {
-            mBkgBounds = new Rect(0, 0, displayLayout.height(),
-                    Math.round(displayLayout.width() * mTranslationFraction) + mStableInsets.top);
-        }
-    }
-
-    @VisibleForTesting
-    void onStart() {
-        if (mBackgroundSurface == null) {
-            createBackgroundSurface();
-        }
-        showBackgroundPanelLayer();
-    }
-
-    /**
-     * Called when transition finished.
-     */
-    public void onStopFinished() {
-        if (mAlphaAnimator == null) {
-            return;
-        }
-        mAlphaAnimator.start();
-    }
-
-    @VisibleForTesting
-    void showBackgroundPanelLayer() {
-        if (mParentLeash == null) {
-            return;
-        }
-
-        if (mBackgroundSurface == null) {
-            createBackgroundSurface();
-        }
-
-        // TODO(185890335) Avoid Dimming for mid-range luminance wallpapers flash.
-        if (mAlphaAnimator.isRunning()) {
-            mAlphaAnimator.end();
-        }
-
-        mTransactionFactory.getTransaction()
-                .reparent(mBackgroundSurface, mParentLeash)
-                .setAlpha(mBackgroundSurface, 1.0f)
-                .setLayer(mBackgroundSurface, -1 /* at bottom-most layer */)
-                .setColor(mBackgroundSurface, mThemeColor)
-                .show(mBackgroundSurface)
-                .apply();
-    }
-
-    @VisibleForTesting
-    void removeBackgroundPanelLayer() {
-        if (mBackgroundSurface == null) {
-            return;
-        }
-
-        mTransactionFactory.getTransaction()
-                .remove(mBackgroundSurface)
-                .apply();
-        mBackgroundSurface = null;
-    }
-
-    /**
-     * onConfigurationChanged events for updating tutorial text.
-     */
-    public void onConfigurationChanged() {
-        updateThemeColors();
-
-        if (mCurrentState != STATE_ACTIVE) {
-            return;
-        }
-        showBackgroundPanelLayer();
-    }
-
-    private void updateThemeColors() {
-        final Context themedContext = new ContextThemeWrapper(mContext,
-                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
-        final int themeColor = themedContext.getColor(
-                R.color.one_handed_tutorial_background_color);
-        mThemeColor = new float[]{
-                adjustColor(Color.red(themeColor)),
-                adjustColor(Color.green(themeColor)),
-                adjustColor(Color.blue(themeColor))};
-    }
-
-    private float adjustColor(int origColor) {
-        return Math.max(origColor - THEME_COLOR_OFFSET, 0) / 255.0f;
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        mCurrentState = newState;
-    }
-
-    void dump(@NonNull PrintWriter pw) {
-        final String innerPrefix = "  ";
-        pw.println(TAG);
-        pw.print(innerPrefix + "mBackgroundSurface=");
-        pw.println(mBackgroundSurface);
-        pw.print(innerPrefix + "mBkgBounds=");
-        pw.println(mBkgBounds);
-        pw.print(innerPrefix + "mThemeColor=");
-        pw.println(mThemeColor);
-        pw.print(innerPrefix + "mTranslationFraction=");
-        pw.println(mTranslationFraction);
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 96f82fa..48acfc1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -99,7 +99,6 @@
 
     private OneHandedEventCallback mEventCallback;
     private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
-    private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
     private OneHandedUiEventLogger mOneHandedUiEventLogger;
 
     private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -163,7 +162,6 @@
                 public void onStopFinished(Rect bounds) {
                     mState.setState(STATE_NONE);
                     notifyShortcutStateChanged(STATE_NONE);
-                    mBackgroundPanelOrganizer.onStopFinished();
                 }
             };
 
@@ -201,32 +199,28 @@
         OneHandedAccessibilityUtil accessibilityUtil = new OneHandedAccessibilityUtil(context);
         OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
         OneHandedState oneHandedState = new OneHandedState();
+        BackgroundWindowManager backgroundWindowManager = new BackgroundWindowManager(context);
         OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
-                settingsUtil, windowManager);
+                settingsUtil, windowManager, backgroundWindowManager);
         OneHandedAnimationController animationController =
                 new OneHandedAnimationController(context);
         OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
                 mainExecutor);
-        OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
-                new OneHandedBackgroundPanelOrganizer(context, displayLayout, settingsUtil,
-                        mainExecutor);
         OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
                 context, displayLayout, settingsUtil, animationController, tutorialHandler,
-                oneHandedBackgroundPanelOrganizer, jankMonitor, mainExecutor);
+                jankMonitor, mainExecutor);
         OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger);
         IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
-        return new OneHandedController(context, displayController,
-                oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
-                settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, jankMonitor,
-                oneHandedUiEventsLogger, overlayManager, taskStackListener, mainExecutor,
-                mainHandler);
+        return new OneHandedController(context, displayController, organizer, touchHandler,
+                tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState,
+                jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener,
+                mainExecutor, mainHandler);
     }
 
     @VisibleForTesting
     OneHandedController(Context context,
             DisplayController displayController,
-            OneHandedBackgroundPanelOrganizer backgroundPanelOrganizer,
             OneHandedDisplayAreaOrganizer displayAreaOrganizer,
             OneHandedTouchHandler touchHandler,
             OneHandedTutorialHandler tutorialHandler,
@@ -243,7 +237,6 @@
         mContext = context;
         mOneHandedSettingsUtil = settingsUtil;
         mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil;
-        mBackgroundPanelOrganizer = backgroundPanelOrganizer;
         mDisplayAreaOrganizer = displayAreaOrganizer;
         mDisplayController = displayController;
         mTouchHandler = touchHandler;
@@ -286,7 +279,6 @@
         mAccessibilityManager.addAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
 
-        mState.addSListeners(mBackgroundPanelOrganizer);
         mState.addSListeners(mTutorialHandler);
     }
 
@@ -368,7 +360,6 @@
                 mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
         mOneHandedAccessibilityUtil.announcementForScreenReader(
                 mOneHandedAccessibilityUtil.getOneHandedStartDescription());
-        mBackgroundPanelOrganizer.onStart();
         mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
         mTimeoutHandler.resetTimer();
         mOneHandedUiEventLogger.writeEvent(
@@ -461,7 +452,6 @@
         }
         mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout);
         mTutorialHandler.onDisplayChanged(newDisplayLayout);
-        mBackgroundPanelOrganizer.onDisplayChanged(newDisplayLayout);
     }
 
     private ContentObserver getObserver(Runnable onChangeRunnable) {
@@ -585,7 +575,6 @@
 
         if (!mIsOneHandedEnabled) {
             mDisplayAreaOrganizer.unregisterOrganizer();
-            mBackgroundPanelOrganizer.unregisterOrganizer();
             // Do NOT register + unRegister DA in the same call
             return;
         }
@@ -594,11 +583,6 @@
             mDisplayAreaOrganizer.registerOrganizer(
                     OneHandedDisplayAreaOrganizer.FEATURE_ONE_HANDED);
         }
-
-        if (!mBackgroundPanelOrganizer.isRegistered()) {
-            mBackgroundPanelOrganizer.registerOrganizer(
-                    OneHandedBackgroundPanelOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL);
-        }
     }
 
     @VisibleForTesting
@@ -613,13 +597,12 @@
     }
 
     private void onConfigChanged(Configuration newConfig) {
-        if (mTutorialHandler == null || mBackgroundPanelOrganizer == null) {
+        if (mTutorialHandler == null) {
             return;
         }
         if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
             return;
         }
-        mBackgroundPanelOrganizer.onConfigurationChanged();
         mTutorialHandler.onConfigurationChanged();
     }
 
@@ -650,10 +633,6 @@
         pw.print(innerPrefix + "mIsSwipeToNotificationEnabled=");
         pw.println(mIsSwipeToNotificationEnabled);
 
-        if (mBackgroundPanelOrganizer != null) {
-            mBackgroundPanelOrganizer.dump(pw);
-        }
-
         if (mDisplayAreaOrganizer != null) {
             mDisplayAreaOrganizer.dump(pw);
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 87eb40c..f61d1b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -80,7 +80,6 @@
             mSurfaceControlTransactionFactory;
     private OneHandedTutorialHandler mTutorialHandler;
     private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
-    private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
 
     @VisibleForTesting
     OneHandedAnimationCallback mOneHandedAnimationCallback =
@@ -135,7 +134,6 @@
             OneHandedSettingsUtil oneHandedSettingsUtil,
             OneHandedAnimationController animationController,
             OneHandedTutorialHandler tutorialHandler,
-            OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
             InteractionJankMonitor jankMonitor,
             ShellExecutor mainExecutor) {
         super(mainExecutor);
@@ -150,7 +148,6 @@
                 SystemProperties.getInt(ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION,
                         animationDurationConfig);
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
-        mBackgroundPanelOrganizer = oneHandedBackgroundGradientOrganizer;
         mTutorialHandler = tutorialHandler;
     }
 
@@ -258,7 +255,6 @@
             animator.setTransitionDirection(direction)
                     .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
                     .addOneHandedAnimationCallback(mTutorialHandler)
-                    .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
                     .setDuration(durationMs)
                     .start();
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index 88f3375..04e8cf9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -32,7 +32,6 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.SystemProperties;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -65,6 +64,7 @@
 
     private final float mTutorialHeightRatio;
     private final WindowManager mWindowManager;
+    private final BackgroundWindowManager mBackgroundWindowManager;
 
     private @OneHandedState.State int mCurrentState;
     private int mTutorialAreaHeight;
@@ -79,9 +79,10 @@
     private int mAlphaAnimationDurationMs;
 
     public OneHandedTutorialHandler(Context context, OneHandedSettingsUtil settingsUtil,
-            WindowManager windowManager) {
+            WindowManager windowManager, BackgroundWindowManager backgroundWindowManager) {
         mContext = context;
         mWindowManager = windowManager;
+        mBackgroundWindowManager = backgroundWindowManager;
         mTutorialHeightRatio = settingsUtil.getTranslationFraction(context);
         mAlphaAnimationDurationMs = settingsUtil.getTransitionDuration(context);
     }
@@ -110,8 +111,19 @@
     }
 
     @Override
+    public void onStartFinished(Rect bounds) {
+        fillBackgroundColor();
+    }
+
+    @Override
+    public void onStopFinished(Rect bounds) {
+        removeBackgroundSurface();
+    }
+
+    @Override
     public void onStateChanged(int newState) {
         mCurrentState = newState;
+        mBackgroundWindowManager.onStateChanged(newState);
         switch (newState) {
             case STATE_ENTERING:
                 createViewAndAttachToWindow(mContext);
@@ -126,7 +138,6 @@
             case STATE_NONE:
                 checkTransitionEnd();
                 removeTutorialFromWindowManager();
-                break;
             default:
                 break;
         }
@@ -146,6 +157,7 @@
         }
         mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
         mAlphaTransitionStart = mTutorialAreaHeight * START_TRANSITION_FRACTION;
+        mBackgroundWindowManager.onDisplayChanged(displayLayout);
     }
 
     @VisibleForTesting
@@ -169,6 +181,7 @@
     private void attachTargetToWindow() {
         try {
             mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
+            mBackgroundWindowManager.showBackgroundLayer();
         } catch (IllegalStateException e) {
             // This shouldn't happen, but if the target is already added, just update its
             // layout params.
@@ -186,6 +199,11 @@
         mTargetViewContainer = null;
     }
 
+    @VisibleForTesting
+    void removeBackgroundSurface() {
+        mBackgroundWindowManager.removeBackgroundLayer();
+    }
+
     /**
      * Returns layout params for the dismiss target, using the latest display metrics.
      */
@@ -213,9 +231,12 @@
      * onConfigurationChanged events for updating tutorial text.
      */
     public void onConfigurationChanged() {
+        mBackgroundWindowManager.onConfigurationChanged();
+
         removeTutorialFromWindowManager();
         if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
             createViewAndAttachToWindow(mContext);
+            fillBackgroundColor();
             updateThemeColor();
             checkTransitionEnd();
         }
@@ -247,6 +268,14 @@
         tutorialDesc.setTextColor(themedTextColorSecondary);
     }
 
+    private void fillBackgroundColor() {
+        if (mTargetViewContainer == null || mBackgroundWindowManager == null) {
+            return;
+        }
+        mTargetViewContainer.setBackgroundColor(
+                mBackgroundWindowManager.getThemeColorForBackground());
+    }
+
     private void setupAlphaTransition(boolean isEntering) {
         final float start = isEntering ? 0.0f : 1.0f;
         final float end = isEntering ? 1.0f : 0.0f;
@@ -282,5 +311,9 @@
         pw.println(mAlphaTransitionStart);
         pw.print(innerPrefix + "mAlphaAnimationDurationMs=");
         pw.println(mAlphaAnimationDurationMs);
+
+        if (mBackgroundWindowManager != null) {
+            mBackgroundWindowManager.dump(pw);
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index 92a3598..915c593 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -140,6 +140,7 @@
         });
 
         mMagnetizedPip = mMotionHelper.getMagnetizedPip();
+        mMagnetizedPip.clearAllTargets();
         mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
         updateMagneticTargetSize();
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 6c9fed9..9a22007 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -22,6 +22,7 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.Test
 
@@ -51,11 +52,28 @@
     @Presubmit
     @Test
     open fun pipWindowBecomesInvisible() {
-        testSpec.assertWm {
-            this.invoke("hasPipWindow") {
-                it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
-            }.then().invoke("!hasPipWindow") {
-                it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+        if (isShellTransitionsEnabled) {
+            // When Shell transition is enabled, we change the windowing mode at start, but
+            // update the visibility after the transition is finished, so we can't check isNotPinned
+            // and isAppWindowInvisible in the same assertion block.
+            testSpec.assertWm {
+                this.invoke("hasPipWindow") {
+                    it.isPinned(pipApp.component)
+                            .isAppWindowVisible(pipApp.component)
+                            .isAppWindowOnTop(pipApp.component)
+                }.then().invoke("!hasPipWindow") {
+                    it.isNotPinned(pipApp.component)
+                            .isAppWindowNotOnTop(pipApp.component)
+                }
+            }
+            testSpec.assertWmEnd { isAppWindowInvisible(pipApp.component) }
+        } else {
+            testSpec.assertWm {
+                this.invoke("hasPipWindow") {
+                    it.isPinned(pipApp.component).isAppWindowVisible(pipApp.component)
+                }.then().invoke("!hasPipWindow") {
+                    it.isNotPinned(pipApp.component).isAppWindowInvisible(pipApp.component)
+                }
             }
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index c75076d..8adebb8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -24,10 +24,7 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
-import org.junit.Assume.assumeFalse
-import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Rule
 import org.junit.Test
@@ -88,20 +85,10 @@
         flickerRule.checkFlakyAssertions()
     }
 
-    @Before
-    fun onBefore() {
-        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
-        assumeFalse(isShellTransitionsEnabled)
-    }
-
     /** {@inheritDoc}  */
     @FlakyTest(bugId = 206753786)
     @Test
-    override fun statusBarLayerRotatesScales() {
-        // This test doesn't work in shell transitions because of b/206753786
-        assumeFalse(isShellTransitionsEnabled)
-        super.statusBarLayerRotatesScales()
-    }
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
 
     companion object {
         /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 52177c2..ef9ff4f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -26,9 +26,6 @@
 import com.android.server.wm.flicker.LAUNCHER_COMPONENT
 import com.android.server.wm.flicker.annotation.Group3
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
-import org.junit.Assume.assumeFalse
-import org.junit.Before
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -65,12 +62,6 @@
             }
         }
 
-    @Before
-    fun onBefore() {
-        // This CUJ don't work in shell transitions because of b/204570898 b/204562589
-        assumeFalse(isShellTransitionsEnabled)
-    }
-
     /**
      * Checks that the pip app window remains inside the display bounds throughout the whole
      * animation
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
new file mode 100644
index 0000000..f3f7067
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/BackgroundWindowManagerTest.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.onehanded;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.TestableLooper;
+
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for {@link BackgroundWindowManager} */
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4.class)
+public class BackgroundWindowManagerTest extends ShellTestCase {
+    private BackgroundWindowManager mBackgroundWindowManager;
+    @Mock
+    private DisplayLayout  mMockDisplayLayout;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mBackgroundWindowManager = new BackgroundWindowManager(mContext);
+        mBackgroundWindowManager.onDisplayChanged(mMockDisplayLayout);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testInitRelease() {
+        mBackgroundWindowManager.initView();
+        assertThat(mBackgroundWindowManager.getSurfaceControl()).isNotNull();
+
+        mBackgroundWindowManager.removeBackgroundLayer();
+        assertThat(mBackgroundWindowManager.getSurfaceControl()).isNull();
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
deleted file mode 100644
index 7b9553c..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizerTest.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.onehanded;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
-
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
-import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.window.DisplayAreaInfo;
-import android.window.IWindowContainerToken;
-import android.window.WindowContainerToken;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayLayout;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OneHandedBackgroundPanelOrganizerTest extends OneHandedTestCase {
-    private DisplayAreaInfo mDisplayAreaInfo;
-    private Display mDisplay;
-    private DisplayLayout mDisplayLayout;
-    private OneHandedBackgroundPanelOrganizer mSpiedBackgroundPanelOrganizer;
-    private WindowContainerToken mToken;
-    private SurfaceControl mLeash;
-
-    @Mock
-    IWindowContainerToken mMockRealToken;
-    @Mock
-    DisplayController mMockDisplayController;
-    @Mock
-    OneHandedSettingsUtil mMockSettingsUtil;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mToken = new WindowContainerToken(mMockRealToken);
-        mLeash = new SurfaceControl();
-        mDisplay = mContext.getDisplay();
-        mDisplayLayout = new DisplayLayout(mContext, mDisplay);
-        when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
-        mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY,
-                FEATURE_ONE_HANDED_BACKGROUND_PANEL);
-
-        mSpiedBackgroundPanelOrganizer = spy(
-                new OneHandedBackgroundPanelOrganizer(mContext, mDisplayLayout, mMockSettingsUtil,
-                        Runnable::run));
-        mSpiedBackgroundPanelOrganizer.onDisplayChanged(mDisplayLayout);
-    }
-
-    @Test
-    public void testOnDisplayAreaAppeared() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
-        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isTrue();
-        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testShowBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, null);
-        mSpiedBackgroundPanelOrganizer.onStart();
-
-        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testRemoveBackgroundLayer() {
-        mSpiedBackgroundPanelOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
-
-        assertThat(mSpiedBackgroundPanelOrganizer.isRegistered()).isNotNull();
-
-        reset(mSpiedBackgroundPanelOrganizer);
-        mSpiedBackgroundPanelOrganizer.removeBackgroundPanelLayer();
-
-        assertThat(mSpiedBackgroundPanelOrganizer.mBackgroundSurface).isNull();
-    }
-
-    @Test
-    public void testStateNone_onConfigurationChanged() {
-        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_NONE);
-        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
-        verify(mSpiedBackgroundPanelOrganizer, never()).showBackgroundPanelLayer();
-    }
-
-    @Test
-    public void testStateActivate_onConfigurationChanged() {
-        mSpiedBackgroundPanelOrganizer.onStateChanged(STATE_ACTIVE);
-        mSpiedBackgroundPanelOrganizer.onConfigurationChanged();
-
-        verify(mSpiedBackgroundPanelOrganizer).showBackgroundPanelLayer();
-    }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 636e875..2886b97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -73,8 +73,6 @@
     @Mock
     DisplayController mMockDisplayController;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
     @Mock
     OneHandedEventCallback mMockEventCallback;
@@ -115,7 +113,6 @@
         when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(null);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
         when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
-        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -134,7 +131,6 @@
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
                 mMockDisplayController,
-                mMockBackgroundOrganizer,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
                 mMockTutorialHandler,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index df21163..9c7f723 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -95,8 +95,6 @@
     @Mock
     WindowContainerTransaction mMockWindowContainerTransaction;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     ShellExecutor mMockShellMainExecutor;
     @Mock
     OneHandedSettingsUtil mMockSettingsUitl;
@@ -143,7 +141,6 @@
                 mMockSettingsUitl,
                 mMockAnimationController,
                 mTutorialHandler,
-                mMockBackgroundOrganizer,
                 mJankMonitor,
                 mMockShellMainExecutor));
 
@@ -431,7 +428,6 @@
                         mMockSettingsUitl,
                         mMockAnimationController,
                         mTutorialHandler,
-                        mMockBackgroundOrganizer,
                         mJankMonitor,
                         mMockShellMainExecutor));
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
index 58399b6..dba1b8b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java
@@ -67,8 +67,6 @@
     @Mock
     DisplayController mMockDisplayController;
     @Mock
-    OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
-    @Mock
     OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
     @Mock
     OneHandedTouchHandler mMockTouchHandler;
@@ -105,7 +103,6 @@
 
         when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
         when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
-        when(mMockBackgroundOrganizer.isRegistered()).thenReturn(true);
         when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
                 mDefaultEnabled);
         when(mMockSettingsUitl.getSettingsOneHandedModeTimeout(any(), anyInt())).thenReturn(
@@ -123,7 +120,6 @@
         mSpiedOneHandedController = spy(new OneHandedController(
                 mContext,
                 mMockDisplayController,
-                mMockBackgroundOrganizer,
                 mMockDisplayAreaOrganizer,
                 mMockTouchHandler,
                 mMockTutorialHandler,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
index b1434ca..63d8bfd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTutorialHandlerTest.java
@@ -56,6 +56,8 @@
     OneHandedSettingsUtil mMockSettingsUtil;
     @Mock
     WindowManager mMockWindowManager;
+    @Mock
+    BackgroundWindowManager mMockBackgroundWindowManager;
 
     @Before
     public void setUp() {
@@ -63,10 +65,11 @@
         when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
 
         mDisplay = mContext.getDisplay();
-        mDisplayLayout = new DisplayLayout(mContext, mDisplay);
+        mDisplayLayout = new DisplayLayout(getTestContext().getApplicationContext(), mDisplay);
         mSpiedTransitionState = spy(new OneHandedState());
         mSpiedTutorialHandler = spy(
-                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager));
+                new OneHandedTutorialHandler(mContext, mMockSettingsUtil, mMockWindowManager,
+                        mMockBackgroundWindowManager));
         mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
     }
 
diff --git a/media/OWNERS b/media/OWNERS
index 0aff43e..5f50137 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -3,7 +3,6 @@
 essick@google.com
 etalvala@google.com
 hdmoon@google.com
-hkuang@google.com
 hunga@google.com
 insun@google.com
 jaewan@google.com
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d2c49e0..af5a3da 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1357,7 +1357,7 @@
             return DEVICE_OUT_BLE_SPEAKER_NAME;
         case DEVICE_OUT_DEFAULT:
         default:
-            return Integer.toString(device);
+            return "0x" + Integer.toHexString(device);
         }
     }
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index ad86002..f3670e6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -764,6 +764,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoAvailable(mSession);
+                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
+                        mSession.getIAppSession().notifyVideoAvailable();
+                    }
                 }
             });
         }
@@ -773,6 +776,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onVideoUnavailable(mSession, reason);
+                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
+                        mSession.getIAppSession().notifyVideoUnavailable(reason);
+                    }
                 }
             });
         }
@@ -782,6 +788,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentAllowed(mSession);
+                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
+                        mSession.getIAppSession().notifyContentAllowed();
+                    }
                 }
             });
         }
@@ -791,6 +800,9 @@
                 @Override
                 public void run() {
                     mSessionCallback.onContentBlocked(mSession, rating);
+                    if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
+                        mSession.getIAppSession().notifyContentBlocked(rating);
+                    }
                 }
             });
         }
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 23201fa..2e04359 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -51,6 +51,10 @@
     void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
     void notifyTrackSelected(in IBinder sessionToken, int type, in String trackId, int userId);
     void notifyTracksChanged(in IBinder sessionToken, in List<TvTrackInfo> tracks, int userId);
+    void notifyVideoAvailable(in IBinder sessionToken, int userId);
+    void notifyVideoUnavailable(in IBinder sessionToken, int reason, int userId);
+    void notifyContentAllowed(in IBinder sessionToken, int userId);
+    void notifyContentBlocked(in IBinder sessionToken, in String rating, int userId);
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
     void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
             int userId);
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 52f9a87..2788ff6 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -42,6 +42,10 @@
     void notifyTuned(in Uri channelUri);
     void notifyTrackSelected(int type, in String trackId);
     void notifyTracksChanged(in List<TvTrackInfo> tracks);
+    void notifyVideoAvailable();
+    void notifyVideoUnavailable(int reason);
+    void notifyContentAllowed();
+    void notifyContentBlocked(in String rating);
     void setSurface(in Surface surface);
     void dispatchSurfaceChanged(int format, int width, int height);
     void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index d1fd1df..9685e3a 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -26,6 +26,7 @@
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
 import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
 import android.net.Uri;
@@ -1037,6 +1038,66 @@
             }
         }
 
+        /**
+         * Notifies IAPP session when video is available.
+         */
+        public void notifyVideoAvailable() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyVideoAvailable(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies IAPP session when video is unavailable.
+         */
+        public void notifyVideoUnavailable(int reason) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyVideoUnavailable(mToken, reason, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies IAPP session when content is allowed.
+         */
+        public void notifyContentAllowed() {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyContentAllowed(mToken, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Notifies IAPP session when content is blocked.
+         */
+        public void notifyContentBlocked(TvContentRating rating) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyContentBlocked(mToken, rating.flattenToString(), mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         private void flushPendingEventsLocked() {
             mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
 
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 0456041..4993bc3 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -31,6 +31,7 @@
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
+import android.media.tv.TvContentRating;
 import android.media.tv.TvTrackInfo;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -442,6 +443,34 @@
         }
 
         /**
+         * Called when video is available.
+         * @hide
+         */
+        public void onVideoAvailable() {
+        }
+
+        /**
+         * Called when video is unavailable.
+         * @hide
+         */
+        public void onVideoUnavailable(int reason) {
+        }
+
+        /**
+         * Called when content is allowed.
+         * @hide
+         */
+        public void onContentAllowed() {
+        }
+
+        /**
+         * Called when content is blocked.
+         * @hide
+         */
+        public void onContentBlocked(TvContentRating rating) {
+        }
+
+        /**
          * Called when a broadcast info response is received.
          * @hide
          */
@@ -816,6 +845,33 @@
             onTracksChanged(tracks);
         }
 
+        void notifyVideoAvailable() {
+            if (DEBUG) {
+                Log.d(TAG, "notifyVideoAvailable");
+            }
+            onVideoAvailable();
+        }
+
+        void notifyVideoUnavailable(int reason) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyVideoAvailable (reason=" + reason + ")");
+            }
+            onVideoUnavailable(reason);
+        }
+
+        void notifyContentAllowed() {
+            if (DEBUG) {
+                Log.d(TAG, "notifyContentAllowed");
+            }
+            notifyContentAllowed();
+        }
+
+        void notifyContentBlocked(TvContentRating rating) {
+            if (DEBUG) {
+                Log.d(TAG, "notifyContentBlocked (rating=" + rating.flattenToString() + ")");
+            }
+            onContentBlocked(rating);
+        }
 
         /**
          * Calls {@link #onBroadcastInfoResponse}.
@@ -1182,6 +1238,26 @@
         }
 
         @Override
+        public void notifyVideoAvailable() {
+            mSessionImpl.notifyVideoAvailable();
+        }
+
+        @Override
+        public void notifyVideoUnavailable(int reason) {
+            mSessionImpl.notifyVideoUnavailable(reason);
+        }
+
+        @Override
+        public void notifyContentAllowed() {
+            mSessionImpl.notifyContentAllowed();
+        }
+
+        @Override
+        public void notifyContentBlocked(String rating) {
+            mSessionImpl.notifyContentBlocked(TvContentRating.unflattenFromString(rating));
+        }
+
+        @Override
         public void setSurface(Surface surface) {
             mSessionImpl.setSurface(surface);
         }
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 1ce14ae..b295055 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -27,6 +27,7 @@
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
 import android.media.tv.interactive.TvIAppManager.Session;
+import android.media.tv.interactive.TvIAppManager.Session.FinishedInputEventCallback;
 import android.media.tv.interactive.TvIAppManager.SessionCallback;
 import android.net.Uri;
 import android.os.Bundle;
@@ -34,11 +35,14 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
+import android.view.InputEvent;
+import android.view.KeyEvent;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewRootImpl;
 
 import java.util.List;
 
@@ -80,6 +84,7 @@
     private final AttributeSet mAttrs;
     private final int mDefStyleAttr;
     private final XmlResourceParser mParser;
+    private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
 
     private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
         @Override
@@ -272,6 +277,77 @@
         mSession.dispatchSurfaceChanged(format, width, height);
     }
 
+    private final FinishedInputEventCallback mFinishedInputEventCallback =
+            new FinishedInputEventCallback() {
+                @Override
+                public void onFinishedInputEvent(Object token, boolean handled) {
+                    if (DEBUG) {
+                        Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled="
+                                + handled + ")");
+                    }
+                    if (handled) {
+                        return;
+                    }
+                    // TODO: Re-order unhandled events.
+                    InputEvent event = (InputEvent) token;
+                    if (dispatchUnhandledInputEvent(event)) {
+                        return;
+                    }
+                    ViewRootImpl viewRootImpl = getViewRootImpl();
+                    if (viewRootImpl != null) {
+                        viewRootImpl.dispatchUnhandledInputEvent(event);
+                    }
+                }
+            };
+
+    /**
+     * Dispatches an unhandled input event to the next receiver.
+     */
+    public boolean dispatchUnhandledInputEvent(@NonNull InputEvent event) {
+        if (mOnUnhandledInputEventListener != null) {
+            if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
+                return true;
+            }
+        }
+        return onUnhandledInputEvent(event);
+    }
+
+    /**
+     * Called when an unhandled input event also has not been handled by the user provided
+     * callback. This is the last chance to handle the unhandled input event in the TvIAppView.
+     *
+     * @param event The input event.
+     * @return If you handled the event, return {@code true}. If you want to allow the event to be
+     *         handled by the next receiver, return {@code false}.
+     */
+    public boolean onUnhandledInputEvent(@NonNull InputEvent event) {
+        return false;
+    }
+
+    /**
+     * Registers a callback to be invoked when an input event is not handled
+     * by the TV Interactive App.
+     *
+     * @param listener The callback to be invoked when the unhandled input event is received.
+     */
+    public void setOnUnhandledInputEventListener(@NonNull OnUnhandledInputEventListener listener) {
+        mOnUnhandledInputEventListener = listener;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(@NonNull KeyEvent event) {
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
+        if (mSession == null) {
+            return false;
+        }
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
+        return ret != Session.DISPATCH_NOT_HANDLED;
+    }
+
     /**
      * Prepares the interactive application.
      */
@@ -514,6 +590,24 @@
          */
         public void onRequestTrackInfoList(@NonNull String iAppServiceId) {
         }
+
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the unhandled input event is received.
+     */
+    public interface OnUnhandledInputEventListener {
+        /**
+         * Called when an input event was not handled by the TV Interactive App.
+         *
+         * <p>This is called asynchronously from where the event is dispatched. It gives the host
+         * application a chance to handle the unhandled input events.
+         *
+         * @param event The input event.
+         * @return If you handled the event, return {@code true}. If you want to allow the event to
+         *         be handled by the next receiver, return {@code false}.
+         */
+        boolean onUnhandledInputEvent(@NonNull InputEvent event);
     }
 
     private class MySessionCallback extends SessionCallback {
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 45c2a5a..9f44236 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -626,8 +626,12 @@
      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
      *
      * @param delayInMs specifies the duration of the delay in milliseconds.
+     * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
+     * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
+     * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
+     * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
      */
-    public int delayCallbackUntilTimeMillis(long delayInMs) {
+    public int delayCallbackUntilMillisElapsed(long delayInMs) {
         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
             return Tuner.RESULT_UNAVAILABLE;
@@ -652,8 +656,12 @@
      * be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
      *
      * @param delayInBytes specifies the duration of the delay in bytes.
+     * @return one of the following results: {@link Tuner#RESULT_SUCCESS},
+     * {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
+     * {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
+     * {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
      */
-    public int delayCallbackUntilBufferFilled(int delayInBytes) {
+    public int delayCallbackUntilBytesAccumulated(int delayInBytes) {
         if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
                   TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
             return Tuner.RESULT_UNAVAILABLE;
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 94fda30..f123675 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,12 +16,13 @@
 
 package android.media.tv.tuner.filter;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.media.tv.tuner.TunerUtils;
 
 /**
- * Filter Settings for Section data according to ISO/IEC 13818-1.
+ * Filter Settings for Section data according to ISO/IEC 13818-1 and ISO/IEC 23008-1.
  *
  * @hide
  */
@@ -30,12 +31,15 @@
     final boolean mCrcEnabled;
     final boolean mIsRepeat;
     final boolean mIsRaw;
+    final int mBitWidthOfLengthField;
 
-    SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw) {
+    SectionSettings(int mainType, boolean crcEnabled, boolean isRepeat, boolean isRaw,
+            int bitWidthOfLengthField) {
         super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
         mCrcEnabled = crcEnabled;
         mIsRepeat = isRepeat;
         mIsRaw = isRaw;
+        mBitWidthOfLengthField = bitWidthOfLengthField;
     }
 
     /**
@@ -62,6 +66,7 @@
     public boolean isRepeat() {
         return mIsRepeat;
     }
+
     /**
      * Returns whether the filter sends {@link FilterCallback#onFilterStatusChanged} instead of
      * {@link FilterCallback#onFilterEvent}.
@@ -71,6 +76,17 @@
     }
 
     /**
+     * Returns the bit width of the MMTP (MPEG Media Transport Protocol) section message's length
+     * field according to ISO/IEC 23008-1.
+     *
+     * The section filter uses this for CRC (Cyclic redundancy check) checking when
+     * {@link #isCrcEnabled()} is {@code true}.
+     */
+    public int getBitWidthOfLengthField() {
+        return mBitWidthOfLengthField;
+    }
+
+    /**
      * Builder for {@link SectionSettings}.
      *
      * @param <T> The subclass to be built.
@@ -80,6 +96,7 @@
         boolean mCrcEnabled;
         boolean mIsRepeat;
         boolean mIsRaw;
+        int mBitWidthOfLengthField;
 
         Builder(int mainType) {
             mMainType = mainType;
@@ -114,6 +131,7 @@
             mIsRepeat = isRepeat;
             return self();
         }
+
         /**
          * Sets whether the filter send onFilterStatus instead of
          * {@link FilterCallback#onFilterEvent}.
@@ -124,6 +142,23 @@
             return self();
         }
 
+        /**
+         * Sets the bit width for the MMTP(MPEG Media Transport Protocol) section message's length
+         * field according to ISO/IEC 23008-1.
+         *
+         * The section filter uses this for CRC (Cyclic redundancy check) checking when
+         * {@link #isCrcEnabled()} is {@code true}.
+         *
+         * <p>This field is only supported in Tuner 2.0 or higher version. Unsupported version will
+         * cause no-op. Use {@link android.media.tv.tuner.TunerVersionChecker#getTunerVersion()}
+         * to get the version information.
+         */
+        @NonNull
+        public T setBitWidthOfLengthField(@IntRange(from = 0) int bitWidthOfLengthField) {
+            mBitWidthOfLengthField = bitWidthOfLengthField;
+            return self();
+        }
+
         /* package */ abstract T self();
     }
 }
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index edfe85e..766a5c9 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -30,10 +30,9 @@
     private final byte[] mMask;
     private final byte[] mMode;
 
-
     private SectionSettingsWithSectionBits(int mainType, boolean isCheckCrc, boolean isRepeat,
-            boolean isRaw, byte[] filter, byte[] mask, byte[] mode) {
-        super(mainType, isCheckCrc, isRepeat, isRaw);
+            boolean isRaw, int bitWidthOfLengthField, byte[] filter, byte[] mask, byte[] mode) {
+        super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField);
         mFilter = filter;
         mMask = mask;
         mMode = mode;
@@ -126,8 +125,8 @@
          */
         @NonNull
         public SectionSettingsWithSectionBits build() {
-            return new SectionSettingsWithSectionBits(
-                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mFilter, mMask, mMode);
+            return new SectionSettingsWithSectionBits(mMainType, mCrcEnabled, mIsRepeat, mIsRaw,
+                    mBitWidthOfLengthField, mFilter, mMask, mMode);
         }
 
         @Override
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index ff8f796..eddef40 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -37,8 +37,8 @@
     private final int mVersion;
 
     private SectionSettingsWithTableInfo(int mainType, boolean isCheckCrc, boolean isRepeat,
-            boolean isRaw, int tableId, int version) {
-        super(mainType, isCheckCrc, isRepeat, isRaw);
+            boolean isRaw, int bitWidthOfLengthField, int tableId, int version) {
+        super(mainType, isCheckCrc, isRepeat, isRaw, bitWidthOfLengthField);
         mTableId = tableId;
         mVersion = version;
     }
@@ -99,8 +99,8 @@
          */
         @NonNull
         public SectionSettingsWithTableInfo build() {
-            return new SectionSettingsWithTableInfo(
-                    mMainType, mCrcEnabled, mIsRepeat, mIsRaw, mTableId, mVersion);
+            return new SectionSettingsWithTableInfo(mMainType, mCrcEnabled, mIsRepeat, mIsRaw,
+                    mBitWidthOfLengthField, mTableId, mVersion);
         }
 
         @Override
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index c601649..d9feaf40 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -461,6 +461,7 @@
 }
 
 MediaEvent::~MediaEvent() {
+    android::Mutex::Autolock autoLock(mLock);
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->DeleteWeakGlobalRef(mMediaEventObj);
     mMediaEventObj = nullptr;
@@ -3515,11 +3516,14 @@
     bool isCheckCrc = env->GetBooleanField(settings, env->GetFieldID(clazz, "mCrcEnabled", "Z"));
     bool isRepeat = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRepeat", "Z"));
     bool isRaw = env->GetBooleanField(settings, env->GetFieldID(clazz, "mIsRaw", "Z"));
+    int32_t bitWidthOfLengthField =
+            env->GetIntField(settings, env->GetFieldID(clazz, "mBitWidthOfLengthField", "I"));
 
     DemuxFilterSectionSettings filterSectionSettings {
         .isCheckCrc = isCheckCrc,
         .isRepeat = isRepeat,
         .isRaw = isRaw,
+        .bitWidthOfLengthField = bitWidthOfLengthField,
     };
     if (env->IsInstanceOf(
             settings,
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 773cdc9..50bb79c 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -275,118 +275,104 @@
         float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
         std::vector<std::any>& garbage)
 {
-    // oldTrack and newTrack are placeholders to be released by garbage without the lock.
-    sp<AudioTrack> oldTrack;
-    sp<AudioTrack> newTrack;
-    status_t status = NO_ERROR;
+    ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
+            " priority=%d, loop=%d, rate=%f)",
+            __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
+            priority, loop, rate);
 
-    {
-        ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
-                " priority=%d, loop=%d, rate=%f)",
-                __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
-                priority, loop, rate);
+    // initialize track
+    const audio_stream_type_t streamType =
+            AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
+    const int32_t channelCount = sound->getChannelCount();
+    const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
+    size_t frameCount = 0;
 
-        // initialize track
-        const audio_stream_type_t streamType =
-                AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
-        const int32_t channelCount = sound->getChannelCount();
-        const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
-        size_t frameCount = 0;
+    if (loop) {
+        const audio_format_t format = sound->getFormat();
+        const size_t frameSize = audio_is_linear_pcm(format)
+                ? channelCount * audio_bytes_per_sample(format) : 1;
+        frameCount = sound->getSizeInBytes() / frameSize;
+    }
 
-        if (loop) {
-            const audio_format_t format = sound->getFormat();
-            const size_t frameSize = audio_is_linear_pcm(format)
-                    ? channelCount * audio_bytes_per_sample(format) : 1;
-            frameCount = sound->getSizeInBytes() / frameSize;
-        }
-
-        // check if the existing track has the same sound id.
-        if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
+    if (mAudioTrack != nullptr) {
+        if (mSoundID == sound->getSoundID()
+                && mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+            // Reuse the old track if the soundID matches.
             // the sample rate may fail to change if the audio track is a fast track.
-            if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
-                newTrack = mAudioTrack;
-                ALOGV("%s: reusing track %p for sound %d",
-                        __func__, mAudioTrack.get(), sound->getSoundID());
-            }
-        }
-        if (newTrack == nullptr) {
-            // mToggle toggles each time a track is started on a given stream.
-            // The toggle is concatenated with the Stream address and passed to AudioTrack
-            // as callback user data. This enables the detection of callbacks received from the old
-            // audio track while the new one is being started and avoids processing them with
-            // wrong audio audio buffer size  (mAudioBufferSize)
-            auto toggle = mToggle ^ 1;
-            // NOLINTNEXTLINE(performance-no-int-to-ptr)
-            void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
-            audio_channel_mask_t soundChannelMask = sound->getChannelMask();
-            // When sound contains a valid channel mask, use it as is.
-            // Otherwise, use stream count to calculate channel mask.
-            audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
-                    ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
-
-            // do not create a new audio track if current track is compatible with sound parameters
-
-            android::content::AttributionSourceState attributionSource;
-            attributionSource.packageName = mStreamManager->getOpPackageName();
-            attributionSource.token = sp<BBinder>::make();
-            // TODO b/182469354 make consistent with AudioRecord, add util for native source
-            newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
-                    channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
-                    staticCallback, userData,
-                    0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
-                    AudioTrack::TRANSFER_DEFAULT,
-                    nullptr /*offloadInfo*/, attributionSource,
-                    mStreamManager->getAttributes(),
-                    false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
-            // Set caller name so it can be logged in destructor.
-            // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
-            newTrack->setCallerName("soundpool");
-            oldTrack = mAudioTrack;
-            status = newTrack->initCheck();
-            if (status != NO_ERROR) {
-                ALOGE("%s: error creating AudioTrack", __func__);
-                // newTrack goes out of scope, so reference count drops to zero
-                goto exit;
-            }
-            // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
-            mToggle = toggle;
-            mAudioTrack = newTrack;
-            ALOGV("%s: using new track %p for sound %d",
-                    __func__, newTrack.get(), sound->getSoundID());
-        }
-        if (mMuted) {
-            newTrack->setVolume(0.0f, 0.0f);
+            ALOGV("%s: reusing track %p for sound %d",
+                    __func__, mAudioTrack.get(), sound->getSoundID());
         } else {
-            newTrack->setVolume(leftVolume, rightVolume);
+            // If reuse not possible, move mAudioTrack to garbage, set to nullptr.
+            garbage.emplace_back(std::move(mAudioTrack));
+            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
         }
-        newTrack->setLoop(0, frameCount, loop);
-        mAudioTrack->start();
-        mSound = sound;
-        mSoundID = sound->getSoundID();
-        mPriority = priority;
-        mLoop = loop;
-        mLeftVolume = leftVolume;
-        mRightVolume = rightVolume;
-        mRate = rate;
-        mState = PLAYING;
-        mStopTimeNs = 0;
-        mStreamID = nextStreamID;  // prefer this to be the last, as it is an atomic sync point
     }
+    if (mAudioTrack == nullptr) {
+        // mToggle toggles each time a track is started on a given stream.
+        // The toggle is concatenated with the Stream address and passed to AudioTrack
+        // as callback user data. This enables the detection of callbacks received from the old
+        // audio track while the new one is being started and avoids processing them with
+        // wrong audio audio buffer size  (mAudioBufferSize)
+        auto toggle = mToggle ^ 1;
+        // NOLINTNEXTLINE(performance-no-int-to-ptr)
+        void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
+        audio_channel_mask_t soundChannelMask = sound->getChannelMask();
+        // When sound contains a valid channel mask, use it as is.
+        // Otherwise, use stream count to calculate channel mask.
+        audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
+                ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
 
-exit:
-    ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
-    if (status != NO_ERROR) {
-        // TODO: should we consider keeping the soundID if the old track is OK?
-        // Do not attempt to restart this track (should we remove the stream id?)
-        mState = IDLE;
-        mSoundID = 0;
-        mSound.reset();
-        mAudioTrack.clear();  // actual release from garbage
+        // do not create a new audio track if current track is compatible with sound parameters
+
+        android::content::AttributionSourceState attributionSource;
+        attributionSource.packageName = mStreamManager->getOpPackageName();
+        attributionSource.token = sp<BBinder>::make();
+        // TODO b/182469354 make consistent with AudioRecord, add util for native source
+        mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
+                channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+                staticCallback, userData,
+                0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
+                AudioTrack::TRANSFER_DEFAULT,
+                nullptr /*offloadInfo*/, attributionSource,
+                mStreamManager->getAttributes(),
+                false /*doNotReconnect*/, 1.0f /*maxRequiredSpeed*/);
+        // Set caller name so it can be logged in destructor.
+        // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
+        mAudioTrack->setCallerName("soundpool");
+
+        if (status_t status = mAudioTrack->initCheck();
+            status != NO_ERROR) {
+            ALOGE("%s: error %d creating AudioTrack", __func__, status);
+            // TODO: should we consider keeping the soundID and reusing the old track?
+            mState = IDLE;
+            mSoundID = 0;
+            mSound.reset();
+            garbage.emplace_back(std::move(mAudioTrack)); // remove mAudioTrack.
+            mAudioTrack.clear(); // move should have cleared the sp<>, but we clear just in case.
+            return;
+        }
+        // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+        mToggle = toggle;
+        ALOGV("%s: using new track %p for sound %d",
+                __func__, mAudioTrack.get(), sound->getSoundID());
     }
-
-    // move tracks to garbage to be released later outside of lock.
-    if (newTrack) garbage.emplace_back(std::move(newTrack));
-    if (oldTrack) garbage.emplace_back(std::move(oldTrack));
+    if (mMuted) {
+        mAudioTrack->setVolume(0.f, 0.f);
+    } else {
+        mAudioTrack->setVolume(leftVolume, rightVolume);
+    }
+    mAudioTrack->setLoop(0, frameCount, loop);
+    mAudioTrack->start();
+    mSound = sound;
+    mSoundID = sound->getSoundID();
+    mPriority = priority;
+    mLoop = loop;
+    mLeftVolume = leftVolume;
+    mRightVolume = rightVolume;
+    mRate = rate;
+    mState = PLAYING;
+    mStopTimeNs = 0;
+    mStreamID = nextStreamID;  // prefer this to be the last, as it is an atomic sync point
 }
 
 /* static */
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 8568383..959e756 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -43,6 +43,7 @@
 }
 
 FilterClient::~FilterClient() {
+    Mutex::Autolock _l(mLock);
     mTunerFilter = nullptr;
     mAvSharedHandle = nullptr;
     mAvSharedMemSize = 0;
@@ -74,6 +75,7 @@
     Result res;
     checkIsPassthroughFilter(configure);
 
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configure(configure);
         res = ClientHelper::getServiceSpecificErrorCode(s);
@@ -87,6 +89,7 @@
 }
 
 Result FilterClient::configureMonitorEvent(int32_t monitorEventType) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -96,6 +99,7 @@
 }
 
 Result FilterClient::configureIpFilterContextId(int32_t cid) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureIpFilterContextId(cid);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -105,6 +109,7 @@
 }
 
 Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->configureAvStreamType(avStreamType);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -114,6 +119,7 @@
 }
 
 Result FilterClient::start() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->start();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -123,6 +129,7 @@
 }
 
 Result FilterClient::stop() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->stop();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -132,6 +139,7 @@
 }
 
 Result FilterClient::flush() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->flush();
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -141,6 +149,7 @@
 }
 
 Result FilterClient::getId(int32_t& id) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->getId(&id);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -150,6 +159,7 @@
 }
 
 Result FilterClient::getId64Bit(int64_t& id) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->getId64Bit(&id);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -159,6 +169,7 @@
 }
 
 Result FilterClient::releaseAvHandle(native_handle_t* handle, uint64_t avDataId) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->releaseAvHandle(dupToAidl(handle), avDataId);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -168,6 +179,7 @@
 }
 
 Result FilterClient::setDataSource(sp<FilterClient> filterClient){
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -177,6 +189,7 @@
 }
 
 Result FilterClient::close() {
+    Mutex::Autolock _l(mLock);
     if (mFilterMQEventFlag != nullptr) {
         EventFlag::deleteEventFlag(&mFilterMQEventFlag);
         mFilterMQEventFlag = nullptr;
@@ -197,6 +210,7 @@
 }
 
 string FilterClient::acquireSharedFilterToken() {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         string filterToken;
         if (mTunerFilter->acquireSharedFilterToken(&filterToken).isOk()) {
@@ -208,6 +222,7 @@
 }
 
 Result FilterClient::freeSharedFilterToken(const string& filterToken) {
+    Mutex::Autolock _l(mLock);
     if (mTunerFilter != nullptr) {
         Status s = mTunerFilter->freeSharedFilterToken(filterToken);
         return ClientHelper::getServiceSpecificErrorCode(s);
@@ -237,6 +252,7 @@
 }
 
 Result FilterClient::getFilterMq() {
+    Mutex::Autolock _l(mLock);
     if (mFilterMQ != nullptr) {
         return Result::SUCCESS;
     }
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 20e5610..9e9b233 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -21,6 +21,7 @@
 #include <aidl/android/media/tv/tuner/BnTunerFilterCallback.h>
 #include <aidl/android/media/tv/tuner/ITunerFilter.h>
 #include <fmq/AidlMessageQueue.h>
+#include <utils/Mutex.h>
 
 #include "ClientHelper.h"
 #include "FilterClientCallback.h"
@@ -37,6 +38,7 @@
 using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
 using ::aidl::android::media::tv::tuner::ITunerFilter;
 using ::android::hardware::EventFlag;
+using ::android::Mutex;
 
 using namespace std;
 
@@ -179,6 +181,7 @@
     uint64_t mAvSharedMemSize;
     bool mIsMediaFilter;
     bool mIsPassthroughFilter;
+    Mutex mLock;
 };
 }  // namespace android
 
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index a316b8a..583f7ba 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -54,6 +54,7 @@
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Provides access to network usage history and statistics. Usage data is collected in
@@ -535,6 +536,31 @@
         return result;
     }
 
+    /**
+     * Query realtime network usage statistics details with interfaces constrains.
+     * Return snapshot of current UID statistics, including any {@link TrafficStats#UID_TETHERING},
+     * video calling data usage and count of network operations that set by
+     * {@link TrafficStats#incrementOperationCount}. The returned data doesn't include any
+     * statistics that is reported by {@link NetworkStatsProvider}.
+     *
+     * @param requiredIfaces A list of interfaces the stats should be restricted to, or
+     *               {@link NetworkStats#INTERFACES_ALL}.
+     *
+     * @hide
+     */
+    //@SystemApi
+    @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+    @NonNull public android.net.NetworkStats getDetailedUidStats(
+                @NonNull Set<String> requiredIfaces) {
+        Objects.requireNonNull(requiredIfaces, "requiredIfaces cannot be null");
+        try {
+            return mService.getDetailedUidStats(requiredIfaces.toArray(new String[0]));
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when get detailed uid stats");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** @hide */
     public void registerUsageCallback(NetworkTemplate template, int networkType,
             long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 97281ed..4db90c1 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -922,6 +922,7 @@
 
     @Override
     public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         try {
             final String[] ifacesToQuery =
                     mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index c206903..3f2b8ac 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -43,4 +43,8 @@
     <color name="settingslib_accent_primary_variant">@android:color/system_accent1_300</color>
 
     <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_50</color>
+
+    <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_200</color>
+
+    <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index 0401098..ec3c336 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -65,4 +65,8 @@
     <color name="settingslib_background_device_default_light">@android:color/system_neutral1_50</color>
 
     <color name="settingslib_text_color_primary_device_default">@android:color/system_neutral1_900</color>
+
+    <color name="settingslib_text_color_secondary_device_default">@android:color/system_neutral2_700</color>
+
+    <color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_600</color>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index 1c33f1a..11546c8 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -20,4 +20,9 @@
     <dimen name="app_icon_min_width">52dp</dimen>
     <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen>
     <dimen name="settingslib_dialogCornerRadius">28dp</dimen>
+
+    <!-- Left padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen>
+    <!-- Right padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 5800636..8e7226b 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -17,13 +17,19 @@
 <resources>
     <style name="TextAppearance.PreferenceTitle.SettingsLib"
            parent="@android:style/TextAppearance.Material.Subhead">
+        <item name="android:textColor">@color/settingslib_text_color_primary_device_default</item>
         <item name="android:fontFamily">@string/settingslib_config_headlineFontFamily</item>
         <item name="android:textSize">20sp</item>
     </style>
 
+    <style name="TextAppearance.PreferenceSummary.SettingsLib"
+           parent="@android:style/TextAppearance.DeviceDefault.Small">
+        <item name="android:textColor">@color/settingslib_text_color_secondary_device_default</item>
+    </style>
+
     <style name="TextAppearance.CategoryTitle.SettingsLib"
            parent="@android:style/TextAppearance.DeviceDefault.Medium">
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@color/settingslib_text_color_preference_category_title</item>
         <item name="android:textSize">14sp</item>
     </style>
 
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 6bf288b..7bf75bc 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -19,8 +19,8 @@
     <!-- Only using in Settings application -->
     <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" >
         <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item>
-        <item name="android:listPreferredItemPaddingStart">24dp</item>
-        <item name="android:listPreferredItemPaddingLeft">24dp</item>
+        <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item>
+        <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item>
         <item name="android:listPreferredItemPaddingEnd">16dp</item>
         <item name="android:listPreferredItemPaddingRight">16dp</item>
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/config.xml b/packages/SettingsLib/SettingsTheme/res/values/config.xml
new file mode 100644
index 0000000..e73dcc0
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <bool name="settingslib_config_icon_space_reserved">true</bool>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
index 18af1f9..f7e0144 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/dimens.xml
@@ -21,4 +21,11 @@
     <dimen name="app_icon_min_width">56dp</dimen>
     <dimen name="two_target_min_width">72dp</dimen>
     <dimen name="settingslib_dialogCornerRadius">8dp</dimen>
+
+    <!-- Left padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingStart">16dp</dimen>
+    <!-- Right padding of the preference -->
+    <dimen name="settingslib_listPreferredItemPaddingEnd">16dp</dimen>
+    <!-- Icon size of the preference -->
+    <dimen name="settingslib_preferenceIconSize">24dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
new file mode 100644
index 0000000..aaab0f0
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2022 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+  -->
+<resources>
+    <style name="TextAppearance.PreferenceTitle.SettingsLib"
+           parent="@android:style/TextAppearance.Material.Subhead">
+    </style>
+
+    <style name="TextAppearance.PreferenceSummary.SettingsLib"
+           parent="@style/PreferenceSummaryTextStyle">
+    </style>
+
+    <style name="TextAppearance.CategoryTitle.SettingsLib"
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+    </style>
+</resources>
diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
index de45ea5..d3fe4a7 100644
--- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
+++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java
@@ -47,7 +47,7 @@
  * Annotation processor for {@link SearchIndexable} that generates {@link SearchIndexableResources}
  * subclasses.
  */
-@SupportedSourceVersion(SourceVersion.RELEASE_8)
+@SupportedSourceVersion(SourceVersion.RELEASE_9)
 @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"})
 public class IndexableProcessor extends AbstractProcessor {
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 011ca0b..cff45c6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,8 +16,6 @@
 
 package com.android.settingslib.net;
 
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.TelephonyManager.SIM_STATE_READY;
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
@@ -27,12 +25,9 @@
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
-import android.net.INetworkStatsService;
-import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkTemplate;
-import android.os.ServiceManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
@@ -51,25 +46,20 @@
 
     private static final String TAG = "DataUsageController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
     private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
     private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
             PERIOD_BUILDER, Locale.getDefault());
 
     private final Context mContext;
-    private final INetworkStatsService mStatsService;
     private final NetworkPolicyManager mPolicyManager;
     private final NetworkStatsManager mNetworkStatsManager;
 
-    private INetworkStatsSession mSession;
     private Callback mCallback;
     private NetworkNameProvider mNetworkController;
     private int mSubscriptionId;
 
     public DataUsageController(Context context) {
         mContext = context;
-        mStatsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
         mPolicyManager = NetworkPolicyManager.from(mContext);
         mNetworkStatsManager = context.getSystemService(NetworkStatsManager.class);
         mSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -112,8 +102,7 @@
     }
 
     public DataUsageInfo getWifiDataUsageInfo() {
-        NetworkTemplate template = NetworkTemplate.buildTemplateWifi(
-                NetworkTemplate.WIFI_NETWORKID_ALL, null);
+        NetworkTemplate template = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
         return getDataUsageInfo(template);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
index 3f95a07..afd44d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageUtils.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.net;
 
 import android.content.Context;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -26,6 +27,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * Utils class for data usage
@@ -73,10 +75,15 @@
 
     private static NetworkTemplate getMobileTemplateForSubId(
             TelephonyManager telephonyManager, int subId) {
-        // The null subscriberId means that no any mobile/carrier network will be matched.
-        // Using old API: buildTemplateMobileAll for the null subscriberId to avoid NPE.
+        // Create template that matches any mobile network when the subscriberId is null.
         String subscriberId = telephonyManager.getSubscriberId(subId);
-        return subscriberId != null ? NetworkTemplate.buildTemplateCarrierMetered(subscriberId)
-                : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+        return subscriberId != null
+                ? new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setSubscriberIds(Set.of(subscriberId))
+                .setMeteredness(NetworkStats.METERED_YES)
+                .build()
+                : new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+                        .setMeteredness(NetworkStats.METERED_YES)
+                        .build();
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
index 21a4ac6..fa4ae67 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
@@ -60,6 +60,22 @@
         return true;
     }
 
+    /**
+     * Confirm Wi-Fi Config is allowed to add according to whether user restriction is set
+     *
+     * @param context A context
+     * @return whether the device is permitted to add new Wi-Fi config
+     */
+    public static boolean isAddWifiConfigAllowed(Context context) {
+        final UserManager userManager = context.getSystemService(UserManager.class);
+        final Bundle restrictions = userManager.getUserRestrictions();
+        if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)) {
+            Log.i(TAG, "Wi-Fi Add network isn't available due to user restriction.");
+            return false;
+        }
+        return true;
+    }
+
     @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU)
     private static boolean isAtLeastT() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
index 7e389a1..919f602 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/NetworkPolicyEditorTest.java
@@ -20,6 +20,7 @@
 
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 
 import androidx.test.InstrumentationRegistry;
@@ -32,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NetworkPolicyEditorTest {
@@ -44,7 +47,9 @@
 
     @Before
     public void setUp() {
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered("123456789123456");
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of("123456789123456")).build();
         NetworkPolicyManager policyManager = NetworkPolicyManager.from(InstrumentationRegistry
                 .getContext());
         mNetworkPolicyEditor = new NetworkPolicyEditor(policyManager);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index 9be783d..f0456b3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -18,26 +18,21 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.usage.NetworkStats;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
-import android.net.INetworkStatsSession;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -47,6 +42,8 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.shadows.ShadowSubscriptionManager;
 
+import java.util.Set;
+
 @RunWith(RobolectricTestRunner.class)
 public class DataUsageControllerTest {
 
@@ -54,8 +51,6 @@
     private static final String SUB_ID_2 = "Test Subscriber 2";
 
     @Mock
-    private INetworkStatsSession mSession;
-    @Mock
     private TelephonyManager mTelephonyManager;
     @Mock
     private SubscriptionManager mSubscriptionManager;
@@ -68,7 +63,6 @@
     private NetworkTemplate mWifiNetworkTemplate;
 
     private DataUsageController mController;
-    private NetworkStatsHistory mNetworkStatsHistory;
     private final int mDefaultSubscriptionId = 1234;
 
     @Before
@@ -80,17 +74,16 @@
                 .thenReturn(mSubscriptionManager);
         when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
         mController = new DataUsageController(mContext);
-        mNetworkStatsHistory = spy(
-                new NetworkStatsHistory(DateUtils.DAY_IN_MILLIS /* bucketDuration */));
-        doReturn(mNetworkStatsHistory)
-                .when(mSession).getHistoryForNetwork(any(NetworkTemplate.class), anyInt());
         ShadowSubscriptionManager.setDefaultDataSubscriptionId(mDefaultSubscriptionId);
         doReturn(SUB_ID).when(mTelephonyManager).getSubscriberId();
 
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
-        mNetworkTemplate2 = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID_2);
-        mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifi(
-                NetworkTemplate.WIFI_NETWORKID_ALL, null);
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(android.net.NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID)).build();
+        mNetworkTemplate2 = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(android.net.NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID_2)).build();
+        mWifiNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index e8d5844..5b0f659 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
+import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.text.format.DateUtils;
 
@@ -40,6 +41,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 
+import java.util.Set;
+
 @RunWith(RobolectricTestRunner.class)
 public class NetworkCycleDataForUidLoaderTest {
     private static final String SUB_ID = "Test Subscriber";
@@ -62,7 +65,9 @@
         when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
                 .thenReturn(mNetworkPolicyManager);
         when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
-        mNetworkTemplate = NetworkTemplate.buildTemplateCarrierMetered(SUB_ID);
+        mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_CARRIER)
+                .setMeteredness(NetworkStats.METERED_YES)
+                .setSubscriberIds(Set.of(SUB_ID)).build();
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
index 3c339de..f6af09a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
@@ -103,4 +103,30 @@
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
     }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForS_shouldReturnTrue() {
+        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
+    }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
+        ReflectionHelpers.setStaticField(
+            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isFalse();
+    }
+
+    @Test
+    public void isAddWifiConfigAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
+        ReflectionHelpers.setStaticField(
+            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(false);
+
+        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 71accc4..00b5f50 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -78,6 +78,8 @@
         Settings.System.NOTIFICATION_SOUND,
         Settings.System.ACCELEROMETER_ROTATION,
         Settings.System.SHOW_BATTERY_PERCENT,
+        Settings.System.ALARM_VIBRATION_INTENSITY,
+        Settings.System.MEDIA_VIBRATION_INTENSITY,
         Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
         Settings.System.RING_VIBRATION_INTENSITY,
         Settings.System.HAPTIC_FEEDBACK_INTENSITY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 84e9d28..6bcb769 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -118,6 +118,8 @@
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.APPLY_RAMPING_RINGER, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+        VALIDATORS.put(System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
         VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 00cdc9b..c5f027b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2916,6 +2916,18 @@
         dumpSetting(s, p,
                 Settings.System.VIBRATE_WHEN_RINGING,
                 SystemSettingsProto.Vibrate.WHEN_RINGING);
+
+        // NOTIFICATION_VIBRATION_INTENSITY is already logged at Notification.vibration_intensity
+        // HAPTIC_FEEDBACK_INTENSITY is already logged at HapticFeedback.intensity
+        dumpSetting(s, p,
+                Settings.System.ALARM_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.ALARM_INTENSITY);
+        dumpSetting(s, p,
+                Settings.System.MEDIA_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.MEDIA_INTENSITY);
+        dumpSetting(s, p,
+                Settings.System.RING_VIBRATION_INTENSITY,
+                SystemSettingsProto.Vibrate.RING_INTENSITY);
         p.end(vibrateToken);
 
         final long volumeToken = p.start(SystemSettingsProto.VOLUME);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1d4a73c..0c70821 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -23,6 +23,7 @@
         >
 
         <!-- Standard permissions granted to the shell. -->
+    <uses-permission android:name="android.permission.LAUNCH_DEVICE_MANAGER_SETUP" />
     <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.SEND_SMS" />
     <uses-permission android:name="android.permission.READ_SMS" />
@@ -607,11 +608,14 @@
     <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
     <uses-permission android:name="android.permission.LOCK_DEVICE" />
 
-    <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
+    <!-- Permissions required for CTS test - CtsSafetyCenterTestCases -->
     <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
-
-    <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
     <uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
+    <uses-permission android:name="android.permission.MANAGE_SAFETY_CENTER" />
+
+
+    <!-- Permission required for CTS test - Notification test suite -->
+    <uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" />
 
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/Shell/res/values-watch/strings.xml b/packages/Shell/res/values-watch/strings.xml
new file mode 100644
index 0000000..5f7bfcb
--- /dev/null
+++ b/packages/Shell/res/values-watch/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <!-- Title for Bug report notification indicating the number of the bug report and the
+       percentage complete. Example: "Bug report #3 is 20% complete" [CHAR LIMIT=50] -->
+    <string name="bugreport_in_progress_title">Bug report <xliff:g id="id" example="#3">#%1$d</xliff:g> is <xliff:g id="percentage" example="20%">%2$s</xliff:g> complete</string>
+</resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index ee9d430..c5a01a1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -199,6 +199,15 @@
      */
     private static final String BUGREPORT_DIR = "bugreports";
 
+    /**
+     * The directory in which System Trace files from the native System Tracing app are stored for
+     * Wear devices.
+     */
+    private static final String WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE = "data/local/traces/";
+
+    /** The directory that contains System Traces in bugreports that include System Traces. */
+    private static final String WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT = "systraces/";
+
     private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
 
     /**
@@ -724,14 +733,16 @@
         nf.setMaximumFractionDigits(2);
         final String percentageText = nf.format((double) info.progress.intValue() / 100);
 
-        String title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
-
-        // TODO: Remove this workaround when notification progress is implemented on Wear.
+        final String title;
         if (mIsWatch) {
+            // TODO: Remove this workaround when notification progress is implemented on Wear.
             nf.setMinimumFractionDigits(0);
             nf.setMaximumFractionDigits(0);
             final String watchPercentageText = nf.format((double) info.progress.intValue() / 100);
-            title = title + "\n" + watchPercentageText;
+            title = mContext.getString(
+                R.string.bugreport_in_progress_title, info.id, watchPercentageText);
+        } else {
+            title = mContext.getString(R.string.bugreport_in_progress_title, info.id);
         }
 
         final String name =
@@ -1456,6 +1467,16 @@
         }
     }
 
+    /** Returns an array of the system trace files collected by the System Tracing native app. */
+    private static File[] getSystemTraceFiles() {
+        try {
+            return new File(WEAR_SYSTEM_TRACES_DIRECTORY_ON_DEVICE).listFiles();
+        } catch (SecurityException e) {
+            Log.e(TAG, "Error getting system trace files.", e);
+            return new File[]{};
+        }
+    }
+
     /**
      * Adds the user-provided info into the bugreport zip file.
      * <p>
@@ -1475,8 +1496,17 @@
             Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
             return;
         }
-        if (TextUtils.isEmpty(info.getTitle()) && TextUtils.isEmpty(info.getDescription())) {
-            Log.d(TAG, "Not touching zip file since neither title nor description are set");
+
+        File[] systemTracesToIncludeInBugreport = new File[] {};
+        if (mIsWatch) {
+            systemTracesToIncludeInBugreport = getSystemTraceFiles();
+            Log.d(TAG, "Found " + systemTracesToIncludeInBugreport.length + " system traces.");
+        }
+
+        if (TextUtils.isEmpty(info.getTitle())
+                    && TextUtils.isEmpty(info.getDescription())
+                    && systemTracesToIncludeInBugreport.length == 0) {
+            Log.d(TAG, "Not touching zip file: no detail to add.");
             return;
         }
         if (info.addedDetailsToZip || info.addingDetailsToZip) {
@@ -1487,7 +1517,10 @@
 
         // It's not possible to add a new entry into an existing file, so we need to create a new
         // zip, copy all entries, then rename it.
-        sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
+        if (!mIsWatch) {
+            // TODO(b/184854609): re-introduce this notification for Wear.
+            sendBugreportBeingUpdatedNotification(mContext, info.id); // ...and that takes time
+        }
 
         final File dir = info.bugreportFile.getParentFile();
         final File tmpZip = new File(dir, "tmp-" + info.bugreportFile.getName());
@@ -1508,6 +1541,13 @@
             }
 
             // Then add the user-provided info.
+            if (systemTracesToIncludeInBugreport.length != 0) {
+                for (File trace : systemTracesToIncludeInBugreport) {
+                    addEntry(zos,
+                            WEAR_SYSTEM_TRACES_DIRECTORY_IN_BUGREPORT + trace.getName(),
+                            new FileInputStream(trace));
+                }
+            }
             addEntry(zos, "title.txt", info.getTitle());
             addEntry(zos, "description.txt", info.getDescription());
         } catch (IOException e) {
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c805e2d..137a1fd 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -129,8 +129,15 @@
 }
 
 filegroup {
+    name: "AAA-src",
+    srcs: ["tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java"],
+    path: "tests/src",
+}
+
+filegroup {
     name: "SystemUI-tests-utils",
     srcs: [
+        "tests/src/com/android/systemui/SysuiBaseFragmentTest.java",
         "tests/src/com/android/systemui/SysuiTestCase.java",
         "tests/src/com/android/systemui/TestableDependency.java",
         "tests/src/com/android/systemui/classifier/FalsingManagerFake.java",
diff --git a/packages/SystemUI/res-keyguard/values/bools.xml b/packages/SystemUI/res-keyguard/values/bools.xml
index 2b83787..c5bf4ce 100644
--- a/packages/SystemUI/res-keyguard/values/bools.xml
+++ b/packages/SystemUI/res-keyguard/values/bools.xml
@@ -17,4 +17,5 @@
 <resources>
     <bool name="kg_show_ime_at_screen_on">true</bool>
     <bool name="kg_use_all_caps">true</bool>
+    <bool name="flag_active_unlock">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 7b8f349..56517cc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -294,6 +294,7 @@
     <string-array name="config_systemUIServiceComponents" translatable="false">
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
+        <item>com.android.keyguard.KeyguardBiometricLockoutLogger</item>
         <item>com.android.systemui.recents.Recents</item>
         <item>com.android.systemui.volume.VolumeUI</item>
         <item>com.android.systemui.statusbar.phone.StatusBar</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2916c1c..7600eb1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1165,7 +1165,7 @@
     <string name="wallet_lockscreen_settings_label">Lock screen settings</string>
 
     <!-- QR Code Scanner label, title [CHAR LIMIT=32] -->
-    <string name="qr_code_scanner_title">QR Code</string>
+    <string name="qr_code_scanner_title">QR code</string>
 
     <!-- QR Code Scanner description [CHAR LIMIT=NONE] -->
     <string name="qr_code_scanner_description">Tap to scan</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 9808374..1d2caf9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -28,6 +28,8 @@
 import android.view.ViewRootImpl;
 import android.view.ViewTreeObserver;
 
+import androidx.annotation.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
 
@@ -60,6 +62,7 @@
     private final Rect mRegisteredSamplingBounds = new Rect();
     private final SamplingCallback mCallback;
     private final Executor mBackgroundExecutor;
+    private final SysuiCompositionSamplingListener mCompositionSamplingListener;
     private boolean mSamplingEnabled = false;
     private boolean mSamplingListenerRegistered = false;
 
@@ -91,9 +94,17 @@
 
     public RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
             Executor backgroundExecutor) {
+        this(sampledView, samplingCallback, sampledView.getContext().getMainExecutor(),
+                backgroundExecutor, new SysuiCompositionSamplingListener());
+    }
+
+    @VisibleForTesting
+    RegionSamplingHelper(View sampledView, SamplingCallback samplingCallback,
+            Executor mainExecutor, Executor backgroundExecutor,
+            SysuiCompositionSamplingListener compositionSamplingListener) {
         mBackgroundExecutor = backgroundExecutor;
-        mSamplingListener = new CompositionSamplingListener(
-                sampledView.getContext().getMainExecutor()) {
+        mCompositionSamplingListener = compositionSamplingListener;
+        mSamplingListener = new CompositionSamplingListener(mainExecutor) {
             @Override
             public void onSampleCollected(float medianLuma) {
                 if (mSamplingEnabled) {
@@ -136,7 +147,7 @@
 
     public void stopAndDestroy() {
         stop();
-        mSamplingListener.destroy();
+        mBackgroundExecutor.execute(mSamplingListener::destroy);
         mIsDestroyed = true;
     }
 
@@ -189,13 +200,12 @@
                 // We only want to re-register if something actually changed
                 unregisterSamplingListener();
                 mSamplingListenerRegistered = true;
-                SurfaceControl wrappedStopLayer = stopLayerControl == null
-                        ? null : new SurfaceControl(stopLayerControl, "regionSampling");
+                SurfaceControl wrappedStopLayer = wrap(stopLayerControl);
                 mBackgroundExecutor.execute(() -> {
                     if (wrappedStopLayer != null && !wrappedStopLayer.isValid()) {
                         return;
                     }
-                    CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
+                    mCompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
                             wrappedStopLayer, mSamplingRequestBounds);
                 });
                 mRegisteredSamplingBounds.set(mSamplingRequestBounds);
@@ -208,14 +218,21 @@
         }
     }
 
+    @VisibleForTesting
+    protected SurfaceControl wrap(SurfaceControl stopLayerControl) {
+        return stopLayerControl == null ? null : new SurfaceControl(stopLayerControl,
+                "regionSampling");
+    }
+
     private void unregisterSamplingListener() {
         if (mSamplingListenerRegistered) {
             mSamplingListenerRegistered = false;
             SurfaceControl wrappedStopLayer = mWrappedStopLayer;
             mRegisteredStopLayer = null;
+            mWrappedStopLayer = null;
             mRegisteredSamplingBounds.setEmpty();
             mBackgroundExecutor.execute(() -> {
-                CompositionSamplingListener.unregister(mSamplingListener);
+                mCompositionSamplingListener.unregister(mSamplingListener);
                 if (wrappedStopLayer != null && wrappedStopLayer.isValid()) {
                     wrappedStopLayer.release();
                 }
@@ -299,4 +316,19 @@
             return true;
         }
     }
+
+    @VisibleForTesting
+    public static class SysuiCompositionSamplingListener {
+        public void register(CompositionSamplingListener listener,
+                int displayId, SurfaceControl stopLayer, Rect samplingArea) {
+            CompositionSamplingListener.register(listener, displayId, stopLayer, samplingArea);
+        }
+
+        /**
+         * Unregisters a sampling listener.
+         */
+        public void unregister(CompositionSamplingListener listener) {
+            CompositionSamplingListener.unregister(listener);
+        }
+    }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
index 22698a8..a274b74 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -89,6 +89,7 @@
 
     override fun destroy() {
         source?.removeCallback(this)
+        source?.destroy()
     }
 
     override fun onTransitionStarted() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
new file mode 100644
index 0000000..214b284
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.content.Context
+import android.hardware.biometrics.BiometricSourceType
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * Logs events when primary authentication requirements change. Primary authentication is considered
+ * authentication using pin/pattern/password input.
+ *
+ * See [PrimaryAuthRequiredEvent] for all the events and their descriptions.
+ */
+@SysUISingleton
+class KeyguardBiometricLockoutLogger @Inject constructor(
+    context: Context?,
+    private val uiEventLogger: UiEventLogger,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val dumpManager: DumpManager
+) : CoreStartable(context) {
+    private var fingerprintLockedOut = false
+    private var faceLockedOut = false
+    private var encryptedOrLockdown = false
+    private var unattendedUpdate = false
+    private var timeout = false
+
+    override fun start() {
+        dumpManager.registerDumpable(this)
+        mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged(
+                KeyguardUpdateMonitor.getCurrentUser())
+        keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback)
+    }
+
+    private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback =
+            object : KeyguardUpdateMonitorCallback() {
+        override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) {
+            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
+                val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut
+                if (lockedOut && !fingerprintLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+                } else if (!lockedOut && fingerprintLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent
+                                    .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+                }
+                fingerprintLockedOut = lockedOut
+            } else if (biometricSourceType == BiometricSourceType.FACE) {
+                val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
+                if (lockedOut && !faceLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+                } else if (!lockedOut && faceLockedOut) {
+                    uiEventLogger.log(
+                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+                }
+                faceLockedOut = lockedOut
+            }
+        }
+
+        override fun onStrongAuthStateChanged(userId: Int) {
+            if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+                return
+            }
+            val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker
+                    .getStrongAuthForUser(userId)
+
+            val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId)
+            if (newEncryptedOrLockdown && !encryptedOrLockdown) {
+                uiEventLogger.log(
+                        PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+            }
+            encryptedOrLockdown = newEncryptedOrLockdown
+
+            val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
+            if (newUnattendedUpdate && !unattendedUpdate) {
+                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+            }
+            unattendedUpdate = newUnattendedUpdate
+
+            val newTimeout = isStrongAuthTimeout(strongAuthFlags)
+            if (newTimeout && !timeout) {
+                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT)
+            }
+            timeout = newTimeout
+        }
+    }
+
+    private fun isUnattendedUpdate(
+        @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
+    ) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+    private fun isStrongAuthTimeout(
+        @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int
+    ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) ||
+            containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT)
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
+        pw.println("  mFingerprintLockedOut=$fingerprintLockedOut")
+        pw.println("  mFaceLockedOut=$faceLockedOut")
+        pw.println("  mIsEncryptedOrLockdown=$encryptedOrLockdown")
+        pw.println("  mIsUnattendedUpdate=$unattendedUpdate")
+        pw.println("  mIsTimeout=$timeout")
+    }
+
+    /**
+     * Events pertaining to whether primary authentication (pin/pattern/password input) is required
+     * for device entry.
+     */
+    @VisibleForTesting
+    enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" +
+                "can persist until the next primary auth or may timeout.")
+        PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924),
+
+        @UiEvent(doc = "Fingerprint can be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925),
+
+        @UiEvent(doc = "Face cannot be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926),
+
+        @UiEvent(doc = "Face can be used to authenticate for device entry.")
+        PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927),
+
+        @UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " +
+                "or a manual user lockdown.")
+        PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928),
+
+        @UiEvent(doc = "Primary authentication is required because it hasn't been used for a " +
+                "time required by a device admin or because primary auth hasn't been used for a " +
+                "time after a non-strong biometric (weak or convenience) is used to unlock the " +
+                "device.")
+        PRIMARY_AUTH_REQUIRED_TIMEOUT(929),
+
+        @UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.")
+        PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931);
+
+        override fun getId(): Int {
+            return mId
+        }
+    }
+
+    companion object {
+        private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean {
+            return strongAuthFlags and flagCheck != 0
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 03f04d3..36fe5ba 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -64,3 +64,19 @@
     val secureCameraLaunched: Boolean,
     val switchingUser: Boolean
 ) : KeyguardListenModel()
+/**
+ * Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
+ */
+data class KeyguardActiveUnlockModel(
+    @CurrentTimeMillisLong override val timeMillis: Long,
+    override val userId: Int,
+    override val listening: Boolean,
+    // keep sorted
+    val authInterruptActive: Boolean,
+    val encryptedOrTimedOut: Boolean,
+    val fpLockout: Boolean,
+    val lockDown: Boolean,
+    val switchingUser: Boolean,
+    val triggerActiveUnlockForAssistant: Boolean,
+    val userCanDismissLockScreen: Boolean
+) : KeyguardListenModel()
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
index f13a59a..210f5e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt
@@ -32,15 +32,17 @@
 ) {
     private val faceQueue = ArrayDeque<KeyguardFaceListenModel>()
     private val fingerprintQueue = ArrayDeque<KeyguardFingerprintListenModel>()
+    private val activeUnlockQueue = ArrayDeque<KeyguardActiveUnlockModel>()
 
     @get:VisibleForTesting val models: List<KeyguardListenModel>
-        get() = faceQueue + fingerprintQueue
+        get() = faceQueue + fingerprintQueue + activeUnlockQueue
 
     /** Push a [model] to the queue (will be logged until the queue exceeds [sizePerModality]). */
     fun add(model: KeyguardListenModel) {
         val queue = when (model) {
             is KeyguardFaceListenModel -> faceQueue.apply { add(model) }
             is KeyguardFingerprintListenModel -> fingerprintQueue.apply { add(model) }
+            is KeyguardActiveUnlockModel -> activeUnlockQueue.apply { add(model) }
         }
 
         if (queue.size > sizePerModality) {
@@ -63,5 +65,9 @@
         for (model in fingerprintQueue) {
             writer.println(stringify(model))
         }
+        writer.println("  Active unlock triggers (last ${activeUnlockQueue.size} calls):")
+        for (model in activeUnlockQueue) {
+            writer.println(stringify(model))
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 98721fd..5276679 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -37,6 +37,8 @@
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.UserSwitchObserver;
 import android.app.admin.DevicePolicyManager;
@@ -102,6 +104,8 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -109,6 +113,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.telephony.TelephonyListenerManager;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.util.RingerModeTracker;
 
 import com.google.android.collect.Lists;
@@ -143,8 +148,10 @@
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final boolean DEBUG_FACE = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_FINGERPRINT = Build.IS_DEBUGGABLE;
+    private static final boolean DEBUG_ACTIVE_UNLOCK = Build.IS_DEBUGGABLE;
     private static final boolean DEBUG_SPEW = false;
     private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
+    private int mNumActiveUnlockTriggers = 0;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
             = "com.android.facelock.FACE_UNLOCK_STARTED";
@@ -183,7 +190,6 @@
     private static final int MSG_USER_STOPPED = 340;
     private static final int MSG_USER_REMOVED = 341;
     private static final int MSG_KEYGUARD_GOING_AWAY = 342;
-    private static final int MSG_LOCK_SCREEN_MODE = 343;
     private static final int MSG_TIME_FORMAT_UPDATE = 344;
     private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
 
@@ -221,7 +227,6 @@
     private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
     public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;
 
-    private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
     /**
      * If no cancel signal has been received after this amount of time, set the biometric running
      * state to stopped to allow Keyguard to retry authentication.
@@ -231,7 +236,6 @@
     private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
             "com.android.settings", "com.android.settings.FallbackHome");
 
-
     /**
      * If true, the system is in the half-boot-to-decryption-screen state.
      * Prudently disable lockscreen.
@@ -334,6 +338,7 @@
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private final Executor mBackgroundExecutor;
     private SensorPrivacyManager mSensorPrivacyManager;
+    private FeatureFlags mFeatureFlags;
     private int mFaceAuthUserId;
 
     /**
@@ -1250,7 +1255,11 @@
                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
     }
 
-    private boolean isEncryptedOrLockdown(int userId) {
+    /**
+     * Returns true if primary authentication is required for the given user due to lockdown
+     * or encryption after reboot.
+     */
+    public boolean isEncryptedOrLockdown(int userId) {
         final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
         final boolean isLockDown =
                 containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
@@ -1311,6 +1320,9 @@
     void setAssistantVisible(boolean assistantVisible) {
         mAssistantVisible = assistantVisible;
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+        if (mAssistantVisible) {
+            requestActiveUnlock();
+        }
     }
 
     static class DisplayClientState {
@@ -1650,6 +1662,7 @@
         Trace.beginSection("KeyguardUpdateMonitor#handleStartedWakingUp");
         Assert.isMainThread();
         updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE);
+        requestActiveUnlock();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1777,7 +1790,8 @@
             AuthController authController,
             TelephonyListenerManager telephonyListenerManager,
             InteractionJankMonitor interactionJankMonitor,
-            LatencyTracker latencyTracker) {
+            LatencyTracker latencyTracker,
+            FeatureFlags featureFlags) {
         mContext = context;
         mSubscriptionManager = SubscriptionManager.from(context);
         mTelephonyListenerManager = telephonyListenerManager;
@@ -1795,6 +1809,7 @@
         mAuthController = authController;
         dumpManager.registerDumpable(getClass().getName(), this);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
+        mFeatureFlags = featureFlags;
 
         mHandler = new Handler(mainLooper) {
             @Override
@@ -2180,6 +2195,7 @@
         }
         mAuthInterruptActive = active;
         updateFaceListeningState(BIOMETRIC_ACTION_UPDATE);
+        requestActiveUnlock();
     }
 
     /**
@@ -2228,6 +2244,97 @@
         }
     }
 
+    /**
+     * Attempts to trigger active unlock.
+     */
+    public void requestActiveUnlock() {
+        // If this message exists, FP has already authenticated, so wait until that is handled
+        if (mHandler.hasMessages(MSG_BIOMETRIC_AUTHENTICATION_CONTINUE)) {
+            return;
+        }
+
+        if (shouldTriggerActiveUnlock() && mFeatureFlags.isEnabled(Flags.ACTIVE_UNLOCK)) {
+            // TODO (b/192405661): call new TrustManager API
+            mNumActiveUnlockTriggers++;
+            Log.d("ActiveUnlock", "would have triggered times=" + mNumActiveUnlockTriggers);
+            showActiveUnlockNotification(mNumActiveUnlockTriggers);
+        }
+    }
+
+    /**
+     * TODO (b/192405661): Only for testing. Remove before release.
+     */
+    private void showActiveUnlockNotification(int times) {
+        final String message = "Active unlock triggered "  + times + " times.";
+        final Notification.Builder nb =
+                new Notification.Builder(mContext, NotificationChannels.GENERAL)
+                        .setSmallIcon(R.drawable.ic_volume_ringer)
+                        .setContentTitle(message)
+                        .setStyle(new Notification.BigTextStyle().bigText(message));
+        mContext.getSystemService(NotificationManager.class).notifyAsUser(
+                "active_unlock",
+                0,
+                nb.build(),
+                UserHandle.ALL);
+    }
+
+    private boolean shouldTriggerActiveUnlock() {
+        // TODO: check if active unlock is ENABLED / AVAILABLE
+
+        // Triggers:
+        final boolean triggerActiveUnlockForAssistant = shouldTriggerActiveUnlockForAssistant();
+        final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
+                && mStatusBarState != StatusBarState.SHADE_LOCKED;
+
+        // Gates:
+        final int user = getCurrentUser();
+
+        // No need to trigger active unlock if we're already unlocked or don't have
+        // pin/pattern/password setup
+        final boolean userCanDismissLockScreen = getUserCanSkipBouncer(user)
+                || !mLockPatternUtils.isSecure(user);
+
+        // Don't trigger active unlock if fp is locked out TODO: confirm this one
+        final boolean fpLockedout = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
+
+        // Don't trigger active unlock if primary auth is required
+        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
+        final boolean isLockDown =
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+        final boolean isEncryptedOrTimedOut =
+                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
+                        || containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
+
+        final boolean shouldTriggerActiveUnlock =
+                (mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
+                        && !mSwitchingUser
+                        && !userCanDismissLockScreen
+                        && !fpLockedout
+                        && !isLockDown
+                        && !isEncryptedOrTimedOut
+                        && !mKeyguardGoingAway
+                        && !mSecureCameraLaunched;
+
+        // Aggregate relevant fields for debug logging.
+        if (DEBUG_ACTIVE_UNLOCK || DEBUG_SPEW) {
+            maybeLogListenerModelData(
+                    new KeyguardActiveUnlockModel(
+                            System.currentTimeMillis(),
+                            user,
+                            shouldTriggerActiveUnlock,
+                            mAuthInterruptActive,
+                            isEncryptedOrTimedOut,
+                            fpLockedout,
+                            isLockDown,
+                            mSwitchingUser,
+                            triggerActiveUnlockForAssistant,
+                            userCanDismissLockScreen));
+        }
+
+        return shouldTriggerActiveUnlock;
+    }
+
     private boolean shouldListenForFingerprintAssistant() {
         BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
         return mAssistantVisible && mKeyguardOccluded
@@ -2242,6 +2349,11 @@
                 && !mUserHasTrust.get(getCurrentUser(), false);
     }
 
+    private boolean shouldTriggerActiveUnlockForAssistant() {
+        return mAssistantVisible && mKeyguardOccluded
+                && !mUserHasTrust.get(getCurrentUser(), false);
+    }
+
     @VisibleForTesting
     protected boolean shouldListenForFingerprint(boolean isUdfps) {
         final int user = getCurrentUser();
@@ -2406,6 +2518,13 @@
             Log.v(TAG, model.toString());
         }
 
+        if (DEBUG_ACTIVE_UNLOCK
+                && model instanceof KeyguardActiveUnlockModel
+                && model.getListening()) {
+            mListenModels.add(model);
+            return;
+        }
+
         // Add model data to the historical buffer.
         final boolean notYetRunning =
                 (DEBUG_FACE
@@ -2514,6 +2633,10 @@
         return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
     }
 
+    public boolean isFaceLockedOut() {
+        return mFaceLockedOutPermanent;
+    }
+
     /**
      * If biometrics hardware is available, not disabled, and user has enrolled templates.
      * This does NOT check if the device is encrypted or in lockdown.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 9dddbb1..c0da57f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.dagger;
 
+import com.android.keyguard.KeyguardBiometricLockoutLogger;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.LatencyTester;
 import com.android.systemui.ScreenDecorations;
@@ -90,6 +91,13 @@
     @ClassKey(KeyguardViewMediator.class)
     public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);
 
+    /** Inject into KeyguardBiometricLockoutLogger. */
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardBiometricLockoutLogger.class)
+    public abstract CoreStartable bindKeyguardBiometricLockoutLogger(
+            KeyguardBiometricLockoutLogger sysui);
+
     /** Inject into LatencyTests. */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 4be819a..5d6c2a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -74,6 +74,9 @@
     public static final ResourceBooleanFlag BOUNCER_USER_SWITCHER =
             new ResourceBooleanFlag(204, R.bool.config_enableBouncerUserSwitcher);
 
+    public static final ResourceBooleanFlag ACTIVE_UNLOCK =
+            new ResourceBooleanFlag(205, R.bool.flag_active_unlock);
+
     /***************************************/
     // 300 - power menu
     public static final BooleanFlag POWER_MENU_LITE =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4658a74..094b192 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -100,6 +100,7 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -2345,16 +2346,20 @@
         // Block the panel from expanding, in case we were doing a swipe to dismiss gesture.
         mKeyguardViewControllerLazy.get().blockPanelExpansionFromCurrentTouch();
         final boolean wasShowing = mShowing;
-        onKeyguardExitFinished();
-
-        if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
-            mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
-        }
-
-        finishSurfaceBehindRemoteAnimation(cancelled);
-        mSurfaceBehindRemoteAnimationRequested = false;
-        mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
         InteractionJankMonitor.getInstance().end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+
+        // Post layout changes to the next frame, so we don't hang at the end of the animation.
+        DejankUtils.postAfterTraversal(() -> {
+            onKeyguardExitFinished();
+
+            if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
+                mKeyguardUnlockAnimationControllerLazy.get().hideKeyguardViewAfterRemoteAnimation();
+            }
+
+            finishSurfaceBehindRemoteAnimation(cancelled);
+            mSurfaceBehindRemoteAnimationRequested = false;
+            mKeyguardUnlockAnimationControllerLazy.get().notifyFinishedKeyguardExitAnimation();
+        });
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 11c4949..bd2f64b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -59,6 +59,7 @@
                                     R.dimen.qs_tile_service_request_tile_width),
                             context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
                     )
+                    isSelected = true
         }
         val spacing = 0
         setView(ll, spacing, spacing, spacing, spacing / 2)
@@ -68,12 +69,17 @@
         val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
         val state = QSTile.BooleanState().apply {
             label = tileData.label
+            handlesLongClick = false
             icon = tileData.icon?.loadDrawable(context)?.let {
                 QSTileImpl.DrawableIcon(it)
             } ?: ResourceIcon.get(R.drawable.android)
         }
         tile.onStateChanged(state)
-        tile.isSelected = true
+        tile.post {
+            tile.stateDescription = ""
+            tile.isClickable = false
+            tile.isSelected = true
+        }
         return tile
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 43a75a5..ad97392 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -24,16 +24,18 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.util.traceSection
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 
 /**
  * Responsible for building and applying the "shade node spec": the list (tree) of things that
  * currently populate the notification shade.
  */
-class ShadeViewManager constructor(
+class ShadeViewManager @AssistedInject constructor(
     context: Context,
-    listContainer: NotificationListContainer,
-    private val stackController: NotifStackController,
+    @Assisted listContainer: NotificationListContainer,
+    @Assisted private val stackController: NotifStackController,
     mediaContainerController: MediaContainerController,
     featureManager: NotificationSectionsFeatureManager,
     logger: ShadeViewDifferLogger,
@@ -68,20 +70,10 @@
     }
 }
 
-class ShadeViewManagerFactory @Inject constructor(
-    private val context: Context,
-    private val logger: ShadeViewDifferLogger,
-    private val mediaContainerController: MediaContainerController,
-    private val sectionsFeatureManager: NotificationSectionsFeatureManager,
-    private val viewBarn: NotifViewBarn
-) {
-    fun create(listContainer: NotificationListContainer, stackController: NotifStackController) =
-        ShadeViewManager(
-            context,
-            listContainer,
-            stackController,
-            mediaContainerController,
-            sectionsFeatureManager,
-            logger,
-            viewBarn)
+@AssistedFactory
+interface ShadeViewManagerFactory {
+    fun create(
+        listContainer: NotificationListContainer,
+        stackController: NotifStackController
+    ): ShadeViewManager
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index bfa4a24..dee1b33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar.phone;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index ec2d036..571c10b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -54,6 +54,7 @@
             isListening = false
             updateListeningState()
             keyguardUpdateMonitor.requestFaceAuth(true)
+            keyguardUpdateMonitor.requestActiveUnlock()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
index cbd6e86..0369d5b 100644
--- a/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
+++ b/packages/SystemUI/tests/src/com/android/AAAPlusPlusVerifySysuiRequiredTestPropertiesTest.java
@@ -110,6 +110,17 @@
     private Collection<String> getClassNamesFromClassPath() {
         ClassPathScanner scanner = new ClassPathScanner(mContext.getPackageCodePath());
 
+        ChainedClassNameFilter filter = makeClassNameFilter();
+
+        try {
+            return scanner.getClassPathEntries(filter);
+        } catch (IOException e) {
+            Log.e(getTag(), "Failed to scan classes", e);
+        }
+        return Collections.emptyList();
+    }
+
+    protected ChainedClassNameFilter makeClassNameFilter() {
         ChainedClassNameFilter filter = new ChainedClassNameFilter();
 
         filter.add(new ExternalClassNameFilter());
@@ -122,13 +133,7 @@
         // the main SystemUI process. Therefore, exclude this package
         // from the base class whitelist.
         filter.add(s -> !s.startsWith("com.android.systemui.screenshot"));
-
-        try {
-            return scanner.getClassPathEntries(filter);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed to scan classes", e);
-        }
-        return Collections.emptyList();
+        return filter;
     }
 
     private String getClsStr() {
@@ -212,8 +217,12 @@
      * as loggable to limit log spam during normal use.
      */
     private void logDebug(String msg) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, msg);
+        if (Log.isLoggable(getTag(), Log.DEBUG)) {
+            Log.d(getTag(), msg);
         }
     }
+
+    protected String getTag() {
+        return TAG;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
new file mode 100644
index 0000000..6bc6505
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard
+
+import android.hardware.biometrics.BiometricSourceType
+import org.mockito.Mockito.verify
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class KeyguardBiometricLockoutLoggerTest : SysuiTestCase() {
+    @Mock
+    lateinit var uiEventLogger: UiEventLogger
+    @Mock
+    lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock
+    lateinit var dumpManager: DumpManager
+    @Mock
+    lateinit var strongAuthTracker: KeyguardUpdateMonitor.StrongAuthTracker
+
+    @Captor
+    lateinit var updateMonitorCallbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+    lateinit var updateMonitorCallback: KeyguardUpdateMonitorCallback
+
+    lateinit var keyguardBiometricLockoutLogger: KeyguardBiometricLockoutLogger
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(keyguardUpdateMonitor.strongAuthTracker).thenReturn(strongAuthTracker)
+        keyguardBiometricLockoutLogger = KeyguardBiometricLockoutLogger(
+                mContext,
+                uiEventLogger,
+                keyguardUpdateMonitor,
+                dumpManager)
+    }
+
+    @Test
+    fun test_logsOnStart() {
+        // GIVEN is encrypted / lockdown before start
+        whenever(keyguardUpdateMonitor.isEncryptedOrLockdown(anyInt()))
+                .thenReturn(true)
+
+        // WHEN start
+        keyguardBiometricLockoutLogger.start()
+
+        // THEN encrypted / lockdown state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
+    }
+
+    @Test
+    fun test_logTimeoutChange() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c timeout
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+    }
+
+    @Test
+    fun test_logUnattendedUpdate() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c unattended update
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+    }
+
+    @Test
+    fun test_logMultipleChanges() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c timeout
+        whenever(strongAuthTracker.getStrongAuthForUser(anyInt()))
+                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+                        or STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE)
+
+        // WHEN primary auth requirement changes
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN primary auth required state is logged with all the reasons
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_TIMEOUT)
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
+
+        // WHEN onStrongAuthStateChanged is called again
+        updateMonitorCallback.onStrongAuthStateChanged(0)
+
+        // THEN no more events are sent since there haven't been any changes
+        verifyNoMoreInteractions(uiEventLogger)
+    }
+
+    @Test
+    fun test_logFaceLockout() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c face lock
+        whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(true)
+
+        // WHEN lockout state changes
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
+
+        // WHEN face lockout is reset
+        whenever(keyguardUpdateMonitor.isFaceLockedOut).thenReturn(false)
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FACE)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET)
+    }
+
+    @Test
+    fun test_logFingerprintLockout() {
+        keyguardBiometricLockoutLogger.start()
+        captureUpdateMonitorCallback()
+
+        // GIVEN primary auth required b/c fingerprint lock
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
+
+        // WHEN lockout state changes
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
+
+        // WHEN fingerprint lockout is reset
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+        updateMonitorCallback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
+
+        // THEN primary auth required state is logged
+        verify(uiEventLogger).log(KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent
+                .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
+    }
+
+    fun captureUpdateMonitorCallback() {
+        verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallbackCaptor.capture())
+        updateMonitorCallback = updateMonitorCallbackCaptor.value
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 70792cf..7266e41 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -88,6 +88,7 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -173,6 +174,8 @@
     private InteractionJankMonitor mInteractionJankMonitor;
     @Mock
     private LatencyTracker mLatencyTracker;
+    @Mock
+    private FeatureFlags mFeatureFlags;
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
     // Direct executor
@@ -1105,7 +1108,7 @@
                     mRingerModeTracker, mBackgroundExecutor, mMainExecutor,
                     mStatusBarStateController, mLockPatternUtils,
                     mAuthController, mTelephonyListenerManager,
-                    mInteractionJankMonitor, mLatencyTracker);
+                    mInteractionJankMonitor, mLatencyTracker, mFeatureFlags);
             setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index 40549d69..8c20b24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -29,8 +29,10 @@
 import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.Rule;
+import org.mockito.Mockito;
 
 public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
 
@@ -78,6 +80,11 @@
         SystemUIFactory.cleanup();
     }
 
+    @AfterClass
+    public static void mockitoTeardown() {
+        Mockito.framework().clearInlineMocks();
+    }
+
     @Override
     protected SysuiTestableContext getContext() {
         return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 8cd8e4d..c0b7271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.dreams;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Intent;
 import android.os.IBinder;
+import android.service.dreams.DreamService;
 import android.service.dreams.IDreamOverlay;
 import android.service.dreams.IDreamOverlayCallback;
 import android.testing.AndroidTestingRunner;
@@ -195,4 +198,22 @@
         mService.onDestroy();
         verify(mDreamOverlayStateController).removeCallback(callbackCapture.getValue());
     }
+
+    @Test
+    public void testShouldShowComplicationsTrueByDefault() {
+        assertThat(mService.shouldShowComplications()).isTrue();
+
+        mService.onBind(new Intent());
+
+        assertThat(mService.shouldShowComplications()).isTrue();
+    }
+
+    @Test
+    public void testShouldShowComplicationsSetByIntentExtra() {
+        final Intent intent = new Intent();
+        intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, false);
+        mService.onBind(intent);
+
+        assertThat(mService.shouldShowComplications()).isFalse();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index f56a185..2ad9c94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -130,4 +130,37 @@
         val tile = content.getChildAt(1) as QSTileView
         assertThat((tile.icon.iconView as ImageView).drawable).isNotNull()
     }
+
+    @Test
+    fun setTileData_hasNoStateDescription() {
+        val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+        val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+        dialog.setTileData(tileData)
+        dialog.show()
+
+        TestableLooper.get(this).processAllMessages()
+
+        val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+        val tile = content.getChildAt(1) as QSTileView
+
+        assertThat(tile.stateDescription).isEqualTo("")
+    }
+
+    @Test
+    fun setTileData_tileNotClickable() {
+        val icon = Icon.createWithResource(mContext, R.drawable.cloud)
+        val tileData = TileRequestDialog.TileData(APP_NAME, LABEL, icon)
+
+        dialog.setTileData(tileData)
+        dialog.show()
+
+        TestableLooper.get(this).processAllMessages()
+
+        val content = dialog.requireViewById<ViewGroup>(TileRequestDialog.CONTENT_ID)
+        val tile = content.getChildAt(1) as QSTileView
+
+        assertThat(tile.isClickable).isFalse()
+        assertThat(tile.isLongClickable).isFalse()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
new file mode 100644
index 0000000..8bc438b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.navigationbar
+import android.graphics.Rect
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewRootImpl
+import androidx.concurrent.futures.DirectExecutor
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class RegionSamplingHelperTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var sampledView: View
+    @Mock
+    lateinit var samplingCallback: RegionSamplingHelper.SamplingCallback
+    @Mock
+    lateinit var compositionListener: RegionSamplingHelper.SysuiCompositionSamplingListener
+    @Mock
+    lateinit var viewRootImpl: ViewRootImpl
+    @Mock
+    lateinit var surfaceControl: SurfaceControl
+    @Mock
+    lateinit var wrappedSurfaceControl: SurfaceControl
+    @JvmField @Rule
+    var rule = MockitoJUnit.rule()
+    lateinit var regionSamplingHelper: RegionSamplingHelper
+
+    @Before
+    fun setup() {
+        whenever(sampledView.isAttachedToWindow).thenReturn(true)
+        whenever(sampledView.viewRootImpl).thenReturn(viewRootImpl)
+        whenever(viewRootImpl.surfaceControl).thenReturn(surfaceControl)
+        whenever(surfaceControl.isValid).thenReturn(true)
+        whenever(wrappedSurfaceControl.isValid).thenReturn(true)
+        whenever(samplingCallback.isSamplingEnabled).thenReturn(true)
+        regionSamplingHelper = object : RegionSamplingHelper(sampledView, samplingCallback,
+                DirectExecutor.INSTANCE, DirectExecutor.INSTANCE, compositionListener) {
+            override fun wrap(stopLayerControl: SurfaceControl?): SurfaceControl {
+                return wrappedSurfaceControl
+            }
+        }
+        regionSamplingHelper.setWindowVisible(true)
+    }
+
+    @Test
+    fun testStart_register() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener).register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_unregister() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.setWindowVisible(false)
+        verify(compositionListener).unregister(any())
+    }
+
+    @Test
+    fun testStart_hasBlur_neverRegisters() {
+        regionSamplingHelper.setWindowHasBlurs(true)
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        verify(compositionListener, never())
+                .register(any(), anyInt(), eq(wrappedSurfaceControl), any())
+    }
+
+    @Test
+    fun testStart_stopAndDestroy() {
+        regionSamplingHelper.start(Rect(0, 0, 100, 100))
+        regionSamplingHelper.stopAndDestroy()
+        verify(compositionListener).unregister(any())
+    }
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 09485d1..aba32ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -667,6 +667,10 @@
                 return null;
             }
 
+            // Don't need to add the embedded hierarchy windows into the accessibility windows list.
+            if (mHostEmbeddedMap.size() > 0 && isEmbeddedHierarchyWindowsLocked(windowId)) {
+                return null;
+            }
             final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
 
             reportedWindow.setId(windowId);
@@ -699,6 +703,21 @@
             return reportedWindow;
         }
 
+        private boolean isEmbeddedHierarchyWindowsLocked(int windowId) {
+            final IBinder leashToken = mWindowIdMap.get(windowId);
+            if (leashToken == null) {
+                return false;
+            }
+
+            for (int i = 0; i < mHostEmbeddedMap.size(); i++) {
+                if (mHostEmbeddedMap.keyAt(i).equals(leashToken)) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
         private int getTypeForWindowManagerWindowType(int windowType) {
             switch (windowType) {
                 case WindowManager.LayoutParams.TYPE_APPLICATION:
@@ -735,8 +754,7 @@
                 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
                 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
                 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
-                case WindowManager.LayoutParams.TYPE_SCREENSHOT:
-                case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
+                case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
                     return AccessibilityWindowInfo.TYPE_SYSTEM;
                 }
 
@@ -748,6 +766,10 @@
                     return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY;
                 }
 
+                case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
+                    return AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY;
+                }
+
                 default: {
                     return -1;
                 }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index bc8da84..262933d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -45,6 +45,7 @@
 import android.bluetooth.IBluetoothManagerCallback;
 import android.bluetooth.IBluetoothProfileServiceConnection;
 import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.IBluetoothLeCallControl;
 import android.content.ActivityNotFoundException;
 import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
@@ -1323,11 +1324,15 @@
                             + bluetoothProfile);
                 }
 
-                if (bluetoothProfile != BluetoothProfile.HEADSET) {
+                Intent intent;
+                if (bluetoothProfile == BluetoothProfile.HEADSET) {
+                    intent = new Intent(IBluetoothHeadset.class.getName());
+                } else if (bluetoothProfile== BluetoothProfile.LE_CALL_CONTROL) {
+                    intent = new Intent(IBluetoothLeCallControl.class.getName());
+                } else {
                     return false;
                 }
 
-                Intent intent = new Intent(IBluetoothHeadset.class.getName());
                 psc = new ProfileServiceConnections(intent);
                 if (!psc.bindService()) {
                     return false;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 5f295ac..e7f4de2 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -26,6 +26,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -47,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
@@ -94,6 +96,7 @@
 
     // Services that should receive lifecycle events.
     private List<SystemService> mServices;
+    private Set<String> mServiceClassnames;
 
     private int mCurrentPhase = -1;
 
@@ -116,6 +119,7 @@
     SystemServiceManager(Context context) {
         mContext = context;
         mServices = new ArrayList<>();
+        mServiceClassnames = new ArraySet<>();
         // Disable using the thread pool for low ram devices
         sUseLifecycleThreadPool = sUseLifecycleThreadPool
                 && !ActivityManager.isLowRamDeviceStatic();
@@ -210,8 +214,17 @@
     }
 
     public void startService(@NonNull final SystemService service) {
+        // Check if already started
+        String className = service.getClass().getName();
+        if (mServiceClassnames.contains(className)) {
+            Slog.i(TAG, "Not starting an already started service " + className);
+            return;
+        }
+        mServiceClassnames.add(className);
+
         // Register it.
         mServices.add(service);
+
         // Start it.
         long time = SystemClock.elapsedRealtime();
         try {
@@ -225,6 +238,7 @@
 
     /** Disallow starting new services after this call. */
     void sealStartedServices() {
+        mServiceClassnames = Collections.emptySet();
         mServices = Collections.unmodifiableList(mServices);
     }
 
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f591b26..297d28d 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
 
+import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.Notification;
@@ -170,6 +171,12 @@
         mAdbConnectionInfo = new AdbConnectionInfo();
     }
 
+    static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
+            @NonNull UserHandle userHandle) {
+        context.sendBroadcastAsUser(intent, userHandle,
+                android.Manifest.permission.MANAGE_DEBUGGING);
+    }
+
     class PairingThread extends Thread implements NsdManager.RegistrationListener {
         private NsdManager mNsdManager;
         private String mPublicKey;
@@ -1278,7 +1285,7 @@
                     ? AdbManager.WIRELESS_STATUS_CONNECTED
                     : AdbManager.WIRELESS_STATUS_DISCONNECTED);
             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void onAdbdWifiServerConnected(int port) {
@@ -1350,7 +1357,8 @@
             if (publicKey == null) {
                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+                        UserHandle.ALL);
             } else {
                 Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
                 intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
@@ -1366,7 +1374,8 @@
                 device.guid = hostname;
                 device.connected = false;
                 intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+                AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+                        UserHandle.ALL);
                 // Add the key into the keystore
                 mAdbKeyStore.setLastConnectionTime(publicKey,
                         System.currentTimeMillis());
@@ -1380,14 +1389,14 @@
             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
                     AdbManager.WIRELESS_STATUS_CONNECTED);
             intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
             Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
             // Map is not serializable, so need to downcast
             intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
 
         private void updateUIPairCode(String code) {
@@ -1397,7 +1406,7 @@
             intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
             intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
                     AdbManager.WIRELESS_STATUS_PAIRING_CODE);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         }
     }
 
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7a4d2ce..2845fbf 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -459,7 +459,7 @@
                 ? AdbManager.WIRELESS_STATUS_CONNECTED
                 : AdbManager.WIRELESS_STATUS_DISCONNECTED);
         intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+        AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
         Slog.i(TAG, "sent port broadcast port=" + port);
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 336572f..8561b61 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -16,6 +16,7 @@
 package com.android.server.am;
 
 import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
@@ -712,8 +713,10 @@
             if (wifiInfo.isValid()) {
                 final long wifiChargeUC = measuredEnergyDeltas != null ?
                         measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
-                mStats.updateWifiState(
-                        extractDeltaLocked(wifiInfo), wifiChargeUC, elapsedRealtime, uptime);
+                final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+                        NetworkStatsManager.class);
+                mStats.updateWifiState(extractDeltaLocked(wifiInfo),
+                        wifiChargeUC, elapsedRealtime, uptime, networkStatsManager);
             } else {
                 Slog.w(TAG, "wifi info is invalid: " + wifiInfo);
             }
@@ -722,8 +725,10 @@
         if (modemInfo != null) {
             final long mobileRadioChargeUC = measuredEnergyDeltas != null
                     ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+            final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
+                    NetworkStatsManager.class);
             mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
-                    uptime);
+                    uptime, networkStatsManager);
         }
 
         if (updateFlags == UPDATE_ALL) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8cb2040..9ffafe25 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -22,6 +22,7 @@
 
 import android.annotation.NonNull;
 import android.app.StatsManager;
+import android.app.usage.NetworkStatsManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -2029,8 +2030,11 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
+            final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+                    NetworkStatsManager.class);
             mHandler.post(() -> {
-                mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime);
+                mStats.updateWifiState(info, POWER_DATA_UNAVAILABLE, elapsedRealtime, uptime,
+                        networkStatsManager);
             });
         }
     }
@@ -2067,9 +2071,11 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
+            final NetworkStatsManager networkStatsManager = mContext.getSystemService(
+                    NetworkStatsManager.class);
             mHandler.post(() -> {
                 mStats.noteModemControllerActivity(info, POWER_DATA_UNAVAILABLE, elapsedRealtime,
-                        uptime);
+                        uptime, networkStatsManager);
             });
         }
     }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c55bbe8..c08cf64 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -539,6 +539,8 @@
      */
     static private native void compactProcess(int pid, int compactionFlags);
 
+    static private native void cancelCompaction();
+
     /**
      * Reads the flag value from DeviceConfig to determine whether app compaction
      * should be enabled, and starts the freeze/compaction thread if needed.
@@ -1049,6 +1051,26 @@
         }
     }
 
+    @GuardedBy({"mService", "mProcLock"})
+    void onOomAdjustChanged(int oldAdj, int newAdj, ProcessRecord app) {
+        // Cancel any currently executing compactions
+        // if the process moved out of cached state
+        if (DefaultProcessDependencies.mPidCompacting == app.mPid && newAdj < oldAdj
+                && newAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+            cancelCompaction();
+        }
+
+        // Perform a minor compaction when a perceptible app becomes the prev/home app
+        // Perform a major compaction when any app enters cached
+        if (oldAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+                && (newAdj == ProcessList.PREVIOUS_APP_ADJ || newAdj == ProcessList.HOME_APP_ADJ)) {
+            compactAppSome(app);
+        } else if (newAdj >= ProcessList.CACHED_APP_MIN_ADJ
+                && newAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
+            compactAppFull(app);
+        }
+    }
+
     @VisibleForTesting
     static final class LastCompactionStats {
         private final long[] mRssAfterCompaction;
@@ -1091,6 +1113,13 @@
                         name = proc.processName;
                         opt.setHasPendingCompact(false);
 
+                        if (mAm.mInternal.isPendingTopUid(proc.uid)) {
+                            // In case the OOM Adjust has not yet been propagated we see if this is
+                            // pending on becoming top app in which case we should not compact.
+                            Slog.e(TAG_AM, "Skip compaction since UID is active for  " + name);
+                            return;
+                        }
+
                         // don't compact if the process has returned to perceptible
                         // and this is only a cached/home/prev compaction
                         if ((pendingAction == COMPACT_PROCESS_SOME
@@ -1500,6 +1529,8 @@
      * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
      */
     private static final class DefaultProcessDependencies implements ProcessDependencies {
+        public static int mPidCompacting = -1;
+
         // Get memory RSS from process.
         @Override
         public long[] getRss(int pid) {
@@ -1509,6 +1540,7 @@
         // Compact process.
         @Override
         public void performCompaction(String action, int pid) throws IOException {
+            mPidCompacting = pid;
             if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
                 compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
@@ -1516,6 +1548,7 @@
             } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
                 compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
             }
+            mPidCompacting = -1;
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index af4ff58..b123496 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2486,18 +2486,9 @@
         // don't compact during bootup
         if (mCachedAppOptimizer.useCompaction() && mService.mBooted) {
             // Cached and prev/home compaction
+            // reminder: here, setAdj is previous state, curAdj is upcoming state
             if (state.getCurAdj() != state.getSetAdj()) {
-                // Perform a minor compaction when a perceptible app becomes the prev/home app
-                // Perform a major compaction when any app enters cached
-                // reminder: here, setAdj is previous state, curAdj is upcoming state
-                if (state.getSetAdj() <= ProcessList.PERCEPTIBLE_APP_ADJ
-                        && (state.getCurAdj() == ProcessList.PREVIOUS_APP_ADJ
-                            || state.getCurAdj() == ProcessList.HOME_APP_ADJ)) {
-                    mCachedAppOptimizer.compactAppSome(app);
-                } else if (state.getCurAdj() >= ProcessList.CACHED_APP_MIN_ADJ
-                        && state.getCurAdj() <= ProcessList.CACHED_APP_MAX_ADJ) {
-                    mCachedAppOptimizer.compactAppFull(app);
-                }
+                mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
             } else if (mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
                     && state.getSetAdj() < ProcessList.FOREGROUND_APP_ADJ
                     // Because these can fire independent of oom_adj/procstate changes, we need
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 9b731d5..b3e46cd 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -174,6 +174,9 @@
     boolean mFgsNotificationWasDeferred;
     // FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
     boolean mFgsNotificationShown;
+    // Whether FGS package has permissions to show notifications.
+    // TODO(b/194833441): Output this field to logs in ActiveServices#logFGSStateChangeLocked.
+    boolean mFgsHasNotificationPermission;
 
     // allow the service becomes foreground service? Service started from background may not be
     // allowed to become a foreground service.
@@ -968,6 +971,9 @@
                     if (nm == null) {
                         return;
                     }
+                    // Record whether the package has permission to notify the user
+                    mFgsHasNotificationPermission = nm.areNotificationsEnabledForPackage(
+                            localPackageName, appUid);
                     Notification localForegroundNoti = _foregroundNoti;
                     try {
                         if (localForegroundNoti.getSmallIcon() == null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0879bec..69765d2 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9319,8 +9319,6 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
-        mAudioSystem.dump(pw);
-
         sLifecycleLogger.dump(pw);
         if (mAudioHandler != null) {
             pw.println("\nMessage handler (watch for unhandled messages):");
@@ -9400,6 +9398,8 @@
         pw.println("mHasSpatializerEffect:" + mHasSpatializerEffect);
         pw.println("isSpatializerEnabled:" + isSpatializerEnabled());
         pw.println("isSpatialAudioEnabled:" + isSpatialAudioEnabled());
+
+        mAudioSystem.dump(pw);
     }
 
     private void dumpSupportedSystemUsage(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index ac212ee..a2ba76b 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -524,11 +524,28 @@
      * @param pw
      */
     public void dump(PrintWriter pw) {
+        pw.println("\nAudioSystemAdapter:");
+        pw.println(" mDevicesForStreamCache:");
+        if (mDevicesForStreamCache != null) {
+            for (Integer stream : mDevicesForStreamCache.keySet()) {
+                pw.println("\t stream:" + stream + " device:"
+                        + AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream)));
+            }
+        }
+        pw.println(" mDevicesForAttrCache:");
+        if (mDevicesForAttrCache != null) {
+            for (AudioAttributes attr : mDevicesForAttrCache.keySet()) {
+                pw.println("\t" + attr);
+                for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) {
+                    pw.println("\t\t" + devAttr);
+                }
+            }
+        }
+
         if (!ENABLE_GETDEVICES_STATS) {
-            // only stats in this dump
+            // only stats in the rest of this dump
             return;
         }
-        pw.println("\nAudioSystemAdapter:");
         for (int i = 0; i < NB_MEASUREMENTS; i++) {
             pw.println(mMethodNames[i]
                     + ": counter=" + mMethodCallCounter[i]
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
index f4997d4..92218b1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -25,7 +25,11 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * Allows clients (such as keyguard) to register for notifications on when biometric lockout
@@ -37,7 +41,8 @@
     private static final String TAG = "LockoutResetTracker";
 
     private final Context mContext;
-    private final ArrayList<ClientCallback> mClientCallbacks;
+    @VisibleForTesting
+    final List<ClientCallback> mClientCallbacks = new ArrayList<>();
 
     private static class ClientCallback {
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
@@ -81,7 +86,6 @@
 
     public LockoutResetDispatcher(Context context) {
         mContext = context;
-        mClientCallbacks = new ArrayList<>();
     }
 
     public void addCallback(IBiometricServiceLockoutResetCallback callback, String opPackageName) {
@@ -106,11 +110,13 @@
     @Override
     public void binderDied(IBinder who) {
         Slog.e(TAG, "Callback binder died: " + who);
-        for (ClientCallback callback : mClientCallbacks) {
+        final Iterator<ClientCallback> iterator = mClientCallbacks.iterator();
+        while (iterator.hasNext()) {
+            final ClientCallback callback = iterator.next();
             if (callback.mCallback.asBinder().equals(who)) {
                 Slog.e(TAG, "Removing dead callback for: " + callback.mOpPackageName);
                 callback.releaseWakelock();
-                mClientCallbacks.remove(callback);
+                iterator.remove();
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6a716cb..066c263 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1223,8 +1223,11 @@
             for (RouteInfo route : mConfig.routes) {
                 lp.addRoute(route);
                 InetAddress address = route.getDestination().getAddress();
-                allowIPv4 |= address instanceof Inet4Address;
-                allowIPv6 |= address instanceof Inet6Address;
+
+                if (route.getType() == RouteInfo.RTN_UNICAST) {
+                    allowIPv4 |= address instanceof Inet4Address;
+                    allowIPv6 |= address instanceof Inet6Address;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index f84b68e..9b0b782 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -35,6 +35,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.util.Log;
 import android.util.Pair;
 
@@ -435,7 +436,7 @@
         public void onHostEndpointConnected(HostEndpointInfo info) {
             try {
                 mHub.onHostEndpointConnected(info);
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "RemoteException in onHostEndpointConnected");
             }
         }
@@ -444,7 +445,7 @@
         public void onHostEndpointDisconnected(short hostEndpointId) {
             try {
                 mHub.onHostEndpointDisconnected((char) hostEndpointId);
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 Log.e(TAG, "RemoteException in onHostEndpointDisconnected");
             }
         }
@@ -457,7 +458,7 @@
                 mHub.sendMessageToHub(contextHubId,
                         ContextHubServiceUtil.createAidlContextHubMessage(hostEndpointId, message));
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
@@ -470,7 +471,7 @@
             try {
                 mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
@@ -481,7 +482,7 @@
             try {
                 mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
@@ -492,7 +493,7 @@
             try {
                 mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
@@ -503,7 +504,7 @@
             try {
                 mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
@@ -513,21 +514,25 @@
             try {
                 mHub.queryNanoapps(contextHubId);
                 return ContextHubTransaction.RESULT_SUCCESS;
-            } catch (RemoteException e) {
+            } catch (RemoteException | ServiceSpecificException e) {
                 return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
             }
         }
 
         public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
             mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback));
-            mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
+            try {
+                mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Exception while registering callback: " + e.getMessage());
+            }
         }
 
         private void onSettingChanged(byte setting, boolean enabled) {
             try {
                 mHub.onSettingChanged(setting, enabled);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while sending setting update");
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Exception while sending setting update: " + e.getMessage());
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 4822d6a..96391ac 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -52,7 +52,6 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
-import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyEvent;
 
@@ -643,22 +642,24 @@
     }
 
     private void pushQueueUpdate() {
-        ParceledListSlice<QueueItem> parcelableQueue;
+        ArrayList<QueueItem> toSend;
         synchronized (mLock) {
             if (mDestroyed) {
                 return;
             }
-            if (mQueue == null) {
-                parcelableQueue = null;
-            } else {
-                parcelableQueue = new ParceledListSlice<>(mQueue);
-                // Limit the size of initial Parcel to prevent binder buffer overflow
-                // as onQueueChanged is an async binder call.
-                parcelableQueue.setInlineCountLimit(1);
+            toSend = new ArrayList<>();
+            if (mQueue != null) {
+                toSend.ensureCapacity(mQueue.size());
+                toSend.addAll(mQueue);
             }
         }
         Collection<ISessionControllerCallbackHolder> deadCallbackHolders = null;
         for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) {
+            ParceledListSlice<QueueItem> parcelableQueue = new ParceledListSlice<>(toSend);
+            // Limit the size of initial Parcel to prevent binder buffer overflow
+            // as onQueueChanged is an async binder call.
+            parcelableQueue.setInlineCountLimit(1);
+
             try {
                 holder.mCallback.onQueueChanged(parcelableQueue);
             } catch (DeadObjectException e) {
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index 2e2d812..8097f4e 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -1,8 +1,6 @@
+# Bug component: 137631
 elaurent@google.com
-hdmoon@google.com
-insun@google.com
-jaewan@google.com
-jinpark@google.com
-klhyun@google.com
 lajos@google.com
-sungsoo@google.com
+
+# go/android-fwk-media-solutions for info on areas of ownership.
+include platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 5455738..c548e7e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -39,4 +39,7 @@
 
     /** Get the number of notification channels for a given package */
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
+
+    /** Does the specified package/uid have permission to post notifications? */
+    boolean areNotificationsEnabledForPackage(String pkg, int uid);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a0eeb65..399ae53 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6205,6 +6205,11 @@
             return NotificationManagerService.this
                     .getNumNotificationChannelsForPackage(pkg, uid, includeDeleted);
         }
+
+        @Override
+        public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
+            return areNotificationsEnabledForPackageInt(pkg, uid);
+        }
     };
 
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 05a51cc..31df0a5 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -896,10 +896,10 @@
         synchronized (mLock) {
             if (!mFinishedPostBootUpdate) {
                 mFinishedPostBootUpdate = true;
-                JobScheduler js = mInjector.getJobScheduler();
-                js.cancel(JOB_POST_BOOT_UPDATE);
             }
         }
+        // Safe to do this outside lock.
+        mInjector.getJobScheduler().cancel(JOB_POST_BOOT_UPDATE);
     }
 
     private void notifyPinService(ArraySet<String> updatedPackages) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 4d6b3c2..416b3a4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -70,6 +70,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -121,7 +122,7 @@
 public class PackageInstallerService extends IPackageInstaller.Stub implements
         PackageSessionProvider {
     private static final String TAG = "PackageInstaller";
-    private static final boolean LOGD = false;
+    private static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final boolean DEBUG = Build.IS_DEBUGGABLE;
 
@@ -510,6 +511,7 @@
                 valid = true;
             }
             if (!valid) {
+                Slog.w(TAG, "Remove old session: " + session.sessionId);
                 // Remove expired sessions as well as child sessions if any
                 mSessions.remove(session.sessionId);
                 // Since this is early during boot we don't send
@@ -850,6 +852,9 @@
         mCallbacks.notifySessionCreated(session.sessionId, session.userId);
 
         mSettingsWriteRequest.schedule();
+        if (LOGD) {
+            Slog.d(TAG, "Created session id=" + sessionId + " staged=" + params.isStaged);
+        }
         return sessionId;
     }
 
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 a3b6b82..85e54f3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -66,9 +66,11 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
+import com.android.server.pm.UserManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
 
@@ -549,6 +551,12 @@
     }
 
     @Override
+    public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
+        mPermissionManagerServiceImpl.revokePostNotificationPermissionWithoutKillForTest(
+                packageName, userId);
+    }
+
+    @Override
     public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
             int userId) {
         return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
@@ -678,8 +686,23 @@
 
         @Override
         public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId,
-                @NonNull PackageInstalledParams params, @UserIdInt int userId) {
-            mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, userId);
+                @NonNull PackageInstalledParams params, @UserIdInt int rawUserId) {
+            Objects.requireNonNull(pkg, "pkg");
+            Objects.requireNonNull(params, "params");
+            Preconditions.checkArgument(rawUserId >= UserHandle.USER_SYSTEM
+                    || rawUserId == UserHandle.USER_ALL, "userId");
+
+            mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, rawUserId);
+            final int[] userIds = rawUserId == UserHandle.USER_ALL ? getAllUserIds()
+                    : new int[] { rawUserId };
+            for (final int userId : userIds) {
+                final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
+                if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
+                        || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
+                    setAutoRevokeExemptedInternal(pkg,
+                            autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
+                }
+            }
         }
 
         @Override
@@ -780,6 +803,15 @@
     }
 
     /**
+     * Returns all relevant user ids.  This list include the current set of created user ids as well
+     * as pre-created user ids.
+     * @return user ids for created users and pre-created users
+     */
+    private int[] getAllUserIds() {
+        return UserManagerService.getInstance().getUserIdsIncludingPreCreated();
+    }
+
+    /**
      * Interface to intercept permission checks and optionally pass through to the original
      * implementation.
      */
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 1b08d77..21cb2c9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -19,8 +19,6 @@
 import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_AUTO_REVOKED;
@@ -63,7 +61,6 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
-import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -170,6 +167,10 @@
     private static final String TAG = "PackageManager";
     private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName();
 
+    private static final String SKIP_KILL_APP_REASON_NOTIFICATION_TEST = "skip permission revoke "
+            + "app kill for notification test";
+
+
     private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
 
     // For automotive products, CarService enforces allow-listing of the privileged permissions
@@ -249,9 +250,6 @@
     /** Permission controller: User space permission management */
     private PermissionControllerManager mPermissionControllerManager;
 
-    /** App ops manager */
-    private final AppOpsManager mAppOpsManager;
-
     /**
      * Built-in permissions. Read from system configuration files. Mapping is from
      * UID to permission name.
@@ -326,11 +324,15 @@
             mPackageManagerInt.writeSettings(true);
         }
         @Override
-        public void onPermissionRevoked(int uid, int userId, String reason) {
+        public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill) {
             mOnPermissionChangeListeners.onPermissionsChanged(uid);
 
             // Critical; after this call the application should never have the permission
             mPackageManagerInt.writeSettings(false);
+            if (overrideKill) {
+                return;
+            }
+
             final int appId = UserHandle.getAppId(uid);
             if (reason == null) {
                 mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
@@ -379,7 +381,6 @@
         mContext = context;
         mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
         mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
-        mAppOpsManager = context.getSystemService(AppOpsManager.class);
 
         mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
         // PackageManager.hasSystemFeature() is not used here because PackageManagerService
@@ -1445,9 +1446,29 @@
                 reason, mDefaultPermissionCallback);
     }
 
+    @Override
+    public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
+        final int callingUid = Binder.getCallingUid();
+        final boolean overridePolicy =
+                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
+                        == PackageManager.PERMISSION_GRANTED;
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, "");
+        revokeRuntimePermissionInternal(packageName, Manifest.permission.POST_NOTIFICATIONS,
+                overridePolicy, true, callingUid, userId,
+                SKIP_KILL_APP_REASON_NOTIFICATION_TEST, mDefaultPermissionCallback);
+    }
+
     private void revokeRuntimePermissionInternal(String packageName, String permName,
-            boolean overridePolicy, int callingUid, final int userId, String reason,
-            PermissionCallback callback) {
+            boolean overridePolicy, int callingUid, final int userId,
+            String reason, PermissionCallback callback) {
+        revokeRuntimePermissionInternal(packageName, permName, overridePolicy, false, callingUid,
+                userId, reason, callback);
+    }
+
+    private void revokeRuntimePermissionInternal(String packageName, String permName,
+            boolean overridePolicy, boolean overrideKill, int callingUid, final int userId,
+            String reason, PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
                 && PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
             Log.i(TAG, "System is revoking " + packageName + " "
@@ -1559,7 +1580,7 @@
         if (callback != null) {
             if (isRuntimePermission) {
                 callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId,
-                        reason);
+                        reason, overrideKill);
             } else {
                 mDefaultPermissionCallback.onInstallPermissionRevoked();
             }
@@ -4924,15 +4945,6 @@
             addAllowlistedRestrictedPermissionsInternal(pkg,
                     params.getAllowlistedRestrictedPermissions(),
                     FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
-            final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode();
-            if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
-                    || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
-                // TODO: theianchen Bug: 182523293
-                // We should move this portion of code that's calling
-                // setAutoRevokeExemptedInternal() into the old PMS
-                setAutoRevokeExemptedInternal(pkg,
-                        autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
-            }
             grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId);
         }
     }
@@ -5201,25 +5213,6 @@
         onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds);
     }
 
-    private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted,
-            @UserIdInt int userId) {
-        final int packageUid = UserHandle.getUid(userId, pkg.getUid());
-        if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
-                packageUid, pkg.getPackageName()) != MODE_ALLOWED) {
-            // Allowlist user set - don't override
-            return false;
-        }
-
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid,
-                    pkg.getPackageName(), exempted ? MODE_IGNORED : MODE_ALLOWED);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-        return true;
-    }
-
     /**
      * Callbacks invoked when interesting actions have been taken on a permission.
      * <p>
@@ -5232,7 +5225,11 @@
         public void onPermissionChanged() {}
         public void onPermissionGranted(int uid, @UserIdInt int userId) {}
         public void onInstallPermissionGranted() {}
-        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {}
+        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
+            onPermissionRevoked(uid, userId, reason, false);
+        }
+        public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason,
+                boolean overrideKill) {}
         public void onInstallPermissionRevoked() {}
         public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {}
         public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index d2018f2..3771f03 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -318,6 +318,15 @@
             String reason);
 
     /**
+     * Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
+     * USED in CTS or local tests.
+     *
+     * @param packageName The package to be revoked
+     * @param userId The user for which to revoke
+     */
+    void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
+
+    /**
      * Get whether you should show UI with rationale for requesting a permission. You should do this
      * only if you do not have the permission and the context in which the permission is requested
      * does not clearly communicate to the user what would be the benefit from grating this
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 10d9bbf..c9a8701 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -1139,13 +1139,11 @@
         private boolean shouldForceShowNotificationPermissionRequest(@NonNull String pkgName,
                 @NonNull UserHandle user) {
             AndroidPackage pkg = mPackageManagerInternal.getPackage(pkgName);
-            // TODO(b/205888750): Remove platform key and permissionController lines after pregrants
-            // are in place
-            if (pkg == null || pkg.getPackageName() == null || pkg.isSignedWithPlatformKey()
-                    || pkg.getPackageName().contains("nexuslauncher")
+            if (pkg == null || pkg.getPackageName() == null
                     || Objects.equals(pkgName, mPackageManager.getPermissionControllerPackageName())
                     || pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
-                // TODO(b/205888750) add warning logs when pregrants in place
+                Slog.w(LOG_TAG, "Cannot check for Notification prompt, no package for "
+                        + pkgName + " or pkg is Permission Controller");
                 return false;
             }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1e74104..4b2770c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -49,6 +49,7 @@
 import android.database.ContentObserver;
 import android.hardware.SensorManager;
 import android.hardware.SystemSensorManager;
+import android.hardware.devicestate.DeviceStateManager;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -60,6 +61,7 @@
 import android.os.BatterySaverPolicyConfig;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.Looper;
@@ -1151,6 +1153,9 @@
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
                 }
+
+                mContext.getSystemService(DeviceStateManager.class).registerCallback(
+                        new HandlerExecutor(mHandler), new DeviceStateListener());
             }
         }
     }
@@ -6349,4 +6354,25 @@
             return interceptPowerKeyDownInternal(event);
         }
     }
+
+    /**
+     * Listens to changes in device state and updates the interactivity time.
+     * Any changes to the device state are treated as user interactions.
+     */
+    class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
+        private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+
+        @Override
+        public void onStateChanged(int deviceState) {
+            if (mDeviceState != deviceState) {
+                mDeviceState = deviceState;
+                // Device-state interactions are applied to the default display so that they
+                // are reflected only with the default power group.
+                userActivityInternal(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_DEVICE_STATE, /* flags= */0,
+                        Process.SYSTEM_UID);
+            }
+        }
+    };
+
 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index ef0079e..ca67597 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -35,7 +35,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
 import com.android.server.powerstats.ProtoStreamUtils.ChannelUtils;
 import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerResultUtils;
@@ -313,12 +312,12 @@
         return mStartWallTime;
     }
 
-    public PowerStatsLogger(Context context, File dataStoragePath,
+    public PowerStatsLogger(Context context, Looper looper, File dataStoragePath,
             String meterFilename, String meterCacheFilename,
             String modelFilename, String modelCacheFilename,
             String residencyFilename, String residencyCacheFilename,
             IPowerStatsHALWrapper powerStatsHALWrapper) {
-        super(Looper.getMainLooper());
+        super(looper);
         mStartWallTime = currentTimeMillis() - SystemClock.elapsedRealtime();
         if (DEBUG) Slog.d(TAG, "mStartWallTime: " + mStartWallTime);
         mPowerStatsHALWrapper = powerStatsHALWrapper;
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index bb52c1d..9953ca8 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -28,6 +28,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.power.PowerStatsInternal;
 import android.util.Slog;
@@ -79,6 +80,9 @@
     private StatsPullAtomCallbackImpl mPullAtomCallback;
     @Nullable
     private PowerStatsInternal mPowerStatsInternal;
+    @Nullable
+    @GuardedBy("this")
+    private Looper mLooper;
 
     @VisibleForTesting
     static class Injector {
@@ -127,12 +131,12 @@
             }
         }
 
-        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String meterFilename, String meterCacheFilename,
+        PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+                File dataStoragePath, String meterFilename, String meterCacheFilename,
                 String modelFilename, String modelCacheFilename,
                 String residencyFilename, String residencyCacheFilename,
                 IPowerStatsHALWrapper powerStatsHALWrapper) {
-            return new PowerStatsLogger(context, dataStoragePath,
+            return new PowerStatsLogger(context, looper, dataStoragePath,
                 meterFilename, meterCacheFilename,
                 modelFilename, modelCacheFilename,
                 residencyFilename, residencyCacheFilename,
@@ -229,11 +233,11 @@
             mDataStoragePath = mInjector.createDataStoragePath();
 
             // Only start logger and triggers if initialization is successful.
-            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
-                mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
-                mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
-                mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
-                getPowerStatsHal());
+            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, getLooper(),
+                    mDataStoragePath, mInjector.createMeterFilename(),
+                    mInjector.createMeterCacheFilename(), mInjector.createModelFilename(),
+                    mInjector.createModelCacheFilename(), mInjector.createResidencyFilename(),
+                    mInjector.createResidencyCacheFilename(), getPowerStatsHal());
             mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
             mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
         } else {
@@ -245,6 +249,17 @@
         return mInjector.getPowerStatsHALWrapperImpl();
     }
 
+    private Looper getLooper() {
+        synchronized (this) {
+            if (mLooper == null) {
+                HandlerThread thread = new HandlerThread(TAG);
+                thread.start();
+                return thread.getLooper();
+            }
+            return mLooper;
+        }
+    }
+
     public PowerStatsService(Context context) {
         this(context, new Injector());
     }
@@ -260,9 +275,7 @@
         private final Handler mHandler;
 
         LocalService() {
-            HandlerThread thread = new HandlerThread(TAG);
-            thread.start();
-            mHandler = new Handler(thread.getLooper());
+            mHandler = new Handler(getLooper());
         }
 
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b1cc517..7f50cd6 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -28,18 +28,11 @@
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkIdentity.OEM_PAID;
 import static android.net.NetworkIdentity.OEM_PRIVATE;
-import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
-import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkTemplate.MATCH_ETHERNET;
-import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
-import static android.net.NetworkTemplate.MATCH_WIFI_WILDCARD;
-import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.net.NetworkTemplate.MATCH_MOBILE;
+import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
-import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
-import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
-import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.NetworkTemplate.getAllCollapsedRatTypes;
 import static android.os.Debug.getIonHeapsSizeKb;
 import static android.os.Process.LAST_SHARED_APPLICATION_GID;
@@ -1179,9 +1172,10 @@
             }
             case FrameworkStatsLog.BYTES_TRANSFER_BY_TAG_AND_METERED: {
                 final NetworkStats wifiStats = getUidNetworkStatsSnapshotForTemplate(
-                        buildTemplateWifiWildcard(), /*includeTags=*/true);
+                        new NetworkTemplate.Builder(MATCH_WIFI).build(), /*includeTags=*/true);
                 final NetworkStats cellularStats = getUidNetworkStatsSnapshotForTemplate(
-                        buildTemplateMobileWildcard(), /*includeTags=*/true);
+                        new NetworkTemplate.Builder(MATCH_MOBILE)
+                        .setMeteredness(METERED_YES).build(), /*includeTags=*/true);
                 if (wifiStats != null && cellularStats != null) {
                     final NetworkStats stats = wifiStats.add(cellularStats);
                     ret.add(new NetworkStatsExt(sliceNetworkStatsByUidTagAndMetered(stats),
@@ -1333,8 +1327,8 @@
     @NonNull private List<NetworkStatsExt> getDataUsageBytesTransferSnapshotForOemManaged() {
         final List<Pair<Integer, Integer>> matchRulesAndTransports = List.of(
                 new Pair(MATCH_ETHERNET, TRANSPORT_ETHERNET),
-                new Pair(MATCH_MOBILE_WILDCARD, TRANSPORT_CELLULAR),
-                new Pair(MATCH_WIFI_WILDCARD, TRANSPORT_WIFI)
+                new Pair(MATCH_MOBILE, TRANSPORT_CELLULAR),
+                new Pair(MATCH_WIFI, TRANSPORT_WIFI)
         );
         final int[] oemManagedTypes = new int[] {OEM_PAID | OEM_PRIVATE, OEM_PAID, OEM_PRIVATE};
 
@@ -1343,12 +1337,11 @@
         for (Pair<Integer, Integer> ruleAndTransport : matchRulesAndTransports) {
             final Integer matchRule = ruleAndTransport.first;
             for (final int oemManaged : oemManagedTypes) {
-                /* A null subscriberId will set wildcard=true, since we aren't trying to select a
-                   specific ssid or subscriber. */
-                final NetworkTemplate template = new NetworkTemplate(matchRule,
-                        /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
-                        METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
-                        oemManaged);
+                // Subscriber Ids and Wifi Network Keys will not be set since the purpose is to
+                // slice statistics of different OEM managed networks among all network types.
+                // Thus, specifying networks through their identifiers are not needed.
+                final NetworkTemplate template = new NetworkTemplate.Builder(matchRule)
+                        .setOemManaged(oemManaged).build();
                 final NetworkStats stats = getUidNetworkStatsSnapshotForTemplate(template, false);
                 final Integer transport = ruleAndTransport.second;
                 if (stats != null) {
@@ -1367,10 +1360,18 @@
      * Create a snapshot of NetworkStats for a given transport.
      */
     @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
-        final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
-                ? NetworkTemplate.buildTemplateMobileWithRatType(
-                /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
-                : NetworkTemplate.buildTemplateWifiWildcard();
+        NetworkTemplate template = null;
+        switch (transport) {
+            case TRANSPORT_CELLULAR:
+                template = new NetworkTemplate.Builder(MATCH_MOBILE)
+                        .setMeteredness(METERED_YES).build();
+                break;
+            case TRANSPORT_WIFI:
+                template = new NetworkTemplate.Builder(MATCH_WIFI).build();
+                break;
+            default:
+                Log.wtf(TAG, "Unexpected transport.");
+        }
         return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
     }
 
@@ -1409,8 +1410,10 @@
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
             final NetworkTemplate template =
-                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
-                    METERED_YES);
+                    new NetworkTemplate.Builder(MATCH_MOBILE)
+                    .setSubscriberIds(Set.of(subInfo.subscriberId))
+                    .setRatType(ratType)
+                    .setMeteredness(METERED_YES).build();
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
             if (stats != null) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index a2bf2fe..a4732c1 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -906,6 +906,94 @@
         }
 
         @Override
+        public void notifyVideoAvailable(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyVideoAvailable");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyVideoAvailable();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyVideoAvailable", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyVideoUnavailable(IBinder sessionToken, int reason, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyVideoUnavailable");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyVideoUnavailable(reason);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyVideoUnavailable", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyContentAllowed(IBinder sessionToken, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyContentAllowed");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyContentAllowed();
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyContentAllowed", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void notifyContentBlocked(IBinder sessionToken, String rating, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyContentBlocked");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyContentBlocked(rating);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyContentBlocked", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void startIApp(IBinder sessionToken, int userId) {
             if (DEBUG) {
                 Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index bd8d13b..6db25b7 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -20,6 +20,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
@@ -44,7 +46,7 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.VcnContext;
 
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
 /** @hide */
@@ -76,7 +78,7 @@
     public static int calculatePriorityClass(
             VcnContext vcnContext,
             UnderlyingNetworkRecord networkRecord,
-            LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -94,7 +96,7 @@
         }
 
         int priorityIndex = 0;
-        for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkPriorities) {
+        for (VcnUnderlyingNetworkTemplate nwPriority : underlyingNetworkTemplates) {
             if (checkMatchesPriorityRule(
                     vcnContext,
                     nwPriority,
@@ -122,7 +124,11 @@
         // TODO: Check Network Quality reported by metric monitors/probers.
 
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
-        if (!networkPriority.allowMetered() && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+
+        final int meteredMatch = networkPriority.getMetered();
+        final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
+        if (meteredMatch == MATCH_REQUIRED && !isMetered
+                || meteredMatch == MATCH_FORBIDDEN && isMetered) {
             return false;
         }
 
@@ -171,7 +177,8 @@
             return false;
         }
 
-        if (networkPriority.getSsid() != null && networkPriority.getSsid() != caps.getSsid()) {
+        if (!networkPriority.getSsids().isEmpty()
+                && !networkPriority.getSsids().contains(caps.getSsid())) {
             return false;
         }
 
@@ -226,26 +233,31 @@
                         .getSystemService(TelephonyManager.class)
                         .createForSubscriptionId(subId);
 
-        if (!networkPriority.getAllowedOperatorPlmnIds().isEmpty()) {
+        if (!networkPriority.getOperatorPlmnIds().isEmpty()) {
             final String plmnId = subIdSpecificTelephonyMgr.getNetworkOperator();
-            if (!networkPriority.getAllowedOperatorPlmnIds().contains(plmnId)) {
+            if (!networkPriority.getOperatorPlmnIds().contains(plmnId)) {
                 return false;
             }
         }
 
-        if (!networkPriority.getAllowedSpecificCarrierIds().isEmpty()) {
+        if (!networkPriority.getSimSpecificCarrierIds().isEmpty()) {
             final int carrierId = subIdSpecificTelephonyMgr.getSimSpecificCarrierId();
-            if (!networkPriority.getAllowedSpecificCarrierIds().contains(carrierId)) {
+            if (!networkPriority.getSimSpecificCarrierIds().contains(carrierId)) {
                 return false;
             }
         }
 
-        if (!networkPriority.allowRoaming() && !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+        final int roamingMatch = networkPriority.getRoaming();
+        final boolean isRoaming = !caps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (roamingMatch == MATCH_REQUIRED && !isRoaming
+                || roamingMatch == MATCH_FORBIDDEN && isRoaming) {
             return false;
         }
 
-        if (networkPriority.requireOpportunistic()) {
-            if (!isOpportunistic(snapshot, caps.getSubscriptionIds())) {
+        final int opportunisticMatch = networkPriority.getOpportunistic();
+        final boolean isOpportunistic = isOpportunistic(snapshot, caps.getSubscriptionIds());
+        if (opportunisticMatch == MATCH_REQUIRED) {
+            if (!isOpportunistic) {
                 return false;
             }
 
@@ -265,6 +277,8 @@
                             .contains(SubscriptionManager.getActiveDataSubscriptionId())) {
                 return false;
             }
+        } else if (opportunisticMatch == MATCH_FORBIDDEN && !isOpportunistic) {
+            return false;
         }
 
         return true;
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index df2f0d5..c0488b1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -32,7 +32,7 @@
 import com.android.server.vcn.VcnContext;
 
 import java.util.Comparator;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -77,7 +77,7 @@
 
     static Comparator<UnderlyingNetworkRecord> getComparator(
             VcnContext vcnContext,
-            LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -87,7 +87,7 @@
                     NetworkPriorityClassifier.calculatePriorityClass(
                             vcnContext,
                             left,
-                            underlyingNetworkPriorities,
+                            underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
                             currentlySelected,
@@ -96,7 +96,7 @@
                     NetworkPriorityClassifier.calculatePriorityClass(
                             vcnContext,
                             right,
-                            underlyingNetworkPriorities,
+                            underlyingNetworkTemplates,
                             subscriptionGroup,
                             snapshot,
                             currentlySelected,
@@ -133,7 +133,7 @@
     void dump(
             VcnContext vcnContext,
             IndentingPrintWriter pw,
-            LinkedHashSet<VcnUnderlyingNetworkTemplate> underlyingNetworkPriorities,
+            List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
@@ -145,7 +145,7 @@
                 NetworkPriorityClassifier.calculatePriorityClass(
                         vcnContext,
                         this,
-                        underlyingNetworkPriorities,
+                        underlyingNetworkTemplates,
                         subscriptionGroup,
                         snapshot,
                         currentlySelected,
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 1ee115d..df6ffa2 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -16,13 +16,16 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
 import static android.os.VibrationAttributes.USAGE_RINGTONE;
 import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.VibrationAttributes.USAGE_UNKNOWN;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -44,8 +47,11 @@
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.os.Vibrator.VibrationIntensity;
+import android.os.vibrator.VibrationConfig;
 import android.provider.Settings;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -109,53 +115,31 @@
     private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
     private final SparseArray<VibrationEffect> mFallbackEffects;
 
-    private final int mRampStepDuration;
-    private final int mRampDownDuration;
+    private final VibrationConfig mVibrationConfig;
 
     @GuardedBy("mLock")
     @Nullable
-    private Vibrator mVibrator;
-    @GuardedBy("mLock")
-    @Nullable
     private AudioManager mAudioManager;
 
     @GuardedBy("mLock")
     private boolean mVibrateInputDevices;
     @GuardedBy("mLock")
-    private boolean mVibrateWhenRinging;
-    @GuardedBy("mLock")
-    private boolean mApplyRampingRinger;
-    @GuardedBy("mLock")
-    private int mHapticFeedbackIntensity;
-    @GuardedBy("mLock")
-    private int mHardwareFeedbackIntensity;
-    @GuardedBy("mLock")
-    private int mNotificationIntensity;
-    @GuardedBy("mLock")
-    private int mRingIntensity;
+    private SparseIntArray mCurrentVibrationIntensities = new SparseIntArray();
     @GuardedBy("mLock")
     private boolean mBatterySaverMode;
 
     VibrationSettings(Context context, Handler handler) {
-        this(context, handler,
-                context.getResources().getInteger(
-                        com.android.internal.R.integer.config_vibrationWaveformRampDownDuration),
-                context.getResources().getInteger(
-                        com.android.internal.R.integer.config_vibrationWaveformRampStepDuration));
+        this(context, handler, new VibrationConfig(context.getResources()));
     }
 
     @VisibleForTesting
-    VibrationSettings(Context context, Handler handler, int rampDownDuration,
-            int rampStepDuration) {
+    VibrationSettings(Context context, Handler handler, VibrationConfig config) {
         mContext = context;
+        mVibrationConfig = config;
         mSettingObserver = new SettingsObserver(handler);
         mUidObserver = new UidObserver();
         mUserReceiver = new UserObserver();
 
-        // TODO(b/191150049): move these to vibrator static config file
-        mRampDownDuration = rampDownDuration;
-        mRampStepDuration = rampStepDuration;
-
         VibrationEffect clickEffect = createEffectFromResource(
                 com.android.internal.R.array.config_virtualKeyVibePattern);
         VibrationEffect doubleClickEffect = createEffectFromResource(
@@ -179,7 +163,6 @@
 
     public void onSystemReady() {
         synchronized (mLock) {
-            mVibrator = mContext.getSystemService(Vibrator.class);
             mAudioManager = mContext.getSystemService(AudioManager.class);
         }
         try {
@@ -214,12 +197,21 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
         mContext.registerReceiver(mUserReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
 
+        // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
         registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
+        registerSettingsObserver(Settings.System.getUriFor(
+                Settings.System.HAPTIC_FEEDBACK_ENABLED));
+        registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.ALARM_VIBRATION_INTENSITY));
         registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
         registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY));
+        registerSettingsObserver(
+                Settings.System.getUriFor(Settings.System.MEDIA_VIBRATION_INTENSITY));
+        registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
         registerSettingsObserver(
                 Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
@@ -253,7 +245,7 @@
      * devices without PWLE support.
      */
     public int getRampStepDuration() {
-        return mRampStepDuration;
+        return mVibrationConfig.getRampStepDurationMs();
     }
 
     /**
@@ -261,7 +253,7 @@
      * when a vibration is cancelled or finished at non-zero amplitude.
      */
     public int getRampDownDuration() {
-        return mRampDownDuration;
+        return mVibrationConfig.getRampDownDurationMs();
     }
 
     /**
@@ -270,25 +262,8 @@
      * @param usageHint one of VibrationAttributes.USAGE_*
      * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
      */
-    public int getDefaultIntensity(int usageHint) {
-        if (usageHint == USAGE_ALARM) {
-            return Vibrator.VIBRATION_INTENSITY_HIGH;
-        }
-        synchronized (mLock) {
-            if (mVibrator != null) {
-                switch (usageHint) {
-                    case USAGE_RINGTONE:
-                        return mVibrator.getDefaultRingVibrationIntensity();
-                    case USAGE_NOTIFICATION:
-                        return mVibrator.getDefaultNotificationVibrationIntensity();
-                    case USAGE_TOUCH:
-                    case USAGE_HARDWARE_FEEDBACK:
-                    case USAGE_PHYSICAL_EMULATION:
-                        return mVibrator.getDefaultHapticFeedbackIntensity();
-                }
-            }
-        }
-        return Vibrator.VIBRATION_INTENSITY_MEDIUM;
+    public int getDefaultIntensity(@VibrationAttributes.Usage int usageHint) {
+        return mVibrationConfig.getDefaultVibrationIntensity(usageHint);
     }
 
     /**
@@ -297,23 +272,10 @@
      * @param usageHint one of VibrationAttributes.USAGE_*
      * @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
      */
-    public int getCurrentIntensity(int usageHint) {
+    public int getCurrentIntensity(@VibrationAttributes.Usage int usageHint) {
+        int defaultIntensity = getDefaultIntensity(usageHint);
         synchronized (mLock) {
-            switch (usageHint) {
-                case USAGE_RINGTONE:
-                    return mRingIntensity;
-                case USAGE_NOTIFICATION:
-                    return mNotificationIntensity;
-                case USAGE_TOUCH:
-                    return mHapticFeedbackIntensity;
-                case USAGE_HARDWARE_FEEDBACK:
-                case USAGE_PHYSICAL_EMULATION:
-                    return mHardwareFeedbackIntensity;
-                case USAGE_ALARM:
-                    return Vibrator.VIBRATION_INTENSITY_HIGH;
-                default:
-                    return Vibrator.VIBRATION_INTENSITY_MEDIUM;
-            }
+            return mCurrentVibrationIntensities.get(usageHint, defaultIntensity);
         }
     }
 
@@ -371,7 +333,7 @@
      * for touch and ringtone usages only. All other usages are allowed by this method.
      */
     @GuardedBy("mLock")
-    private boolean shouldVibrateForRingerModeLocked(int usageHint) {
+    private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
         // If audio manager was not loaded yet then assume most restrictive mode.
         int ringerMode = (mAudioManager == null)
                 ? AudioManager.RINGER_MODE_SILENT
@@ -379,18 +341,9 @@
 
         switch (usageHint) {
             case USAGE_TOUCH:
-                // Touch feedback disabled when phone is on silent mode.
-                return ringerMode != AudioManager.RINGER_MODE_SILENT;
             case USAGE_RINGTONE:
-                switch (ringerMode) {
-                    case AudioManager.RINGER_MODE_SILENT:
-                        return false;
-                    case AudioManager.RINGER_MODE_VIBRATE:
-                        return true;
-                    default:
-                        // Ringtone vibrations also depend on 2 other settings:
-                        return mVibrateWhenRinging || mApplyRampingRinger;
-                }
+                // Touch feedback and ringtone disabled when phone is on silent mode.
+                return ringerMode != AudioManager.RINGER_MODE_SILENT;
             default:
                 // All other usages ignore ringer mode settings.
                 return true;
@@ -401,64 +354,89 @@
     @VisibleForTesting
     void updateSettings() {
         synchronized (mLock) {
-            mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
-            mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
-            mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                    getDefaultIntensity(USAGE_TOUCH));
-            mHardwareFeedbackIntensity = getSystemSetting(
-                    Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
-                    getHardwareFeedbackIntensityWhenSettingIsMissing(mHapticFeedbackIntensity));
-            mNotificationIntensity = getSystemSetting(
-                    Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                    getDefaultIntensity(USAGE_NOTIFICATION));
-            mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
+            mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+
+            int alarmIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
+                    getDefaultIntensity(USAGE_ALARM));
+            int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);
+            int hapticFeedbackIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1),
+                    defaultHapticFeedbackIntensity);
+            int positiveHapticFeedbackIntensity = toPositiveIntensity(
+                    hapticFeedbackIntensity, defaultHapticFeedbackIntensity);
+            int hardwareFeedbackIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1),
+                    positiveHapticFeedbackIntensity);
+            int mediaIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1),
+                    getDefaultIntensity(USAGE_MEDIA));
+            int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);
+            int notificationIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1),
+                    defaultNotificationIntensity);
+            int positiveNotificationIntensity = toPositiveIntensity(
+                    notificationIntensity, defaultNotificationIntensity);
+            int ringIntensity = toIntensity(
+                    loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
                     getDefaultIntensity(USAGE_RINGTONE));
-            mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
+
+
+            mCurrentVibrationIntensities.clear();
+            mCurrentVibrationIntensities.put(USAGE_ALARM, alarmIntensity);
+            mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity);
+            mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity);
+            mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity);
+
+            // Communication request is not disabled by the notification setting.
+            mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST,
+                    positiveNotificationIntensity);
+
+            if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING)
+                    && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) {
+                // Make sure deprecated boolean setting still disables ringtone vibrations.
+                mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF);
+            } else {
+                mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity);
+            }
+
+            // This should adapt the behavior preceding the introduction of this new setting
+            // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled.
+            mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
+            mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity);
+
+            if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) {
+                // Make sure deprecated boolean setting still disables touch vibrations.
+                mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);
+            } else {
+                mCurrentVibrationIntensities.put(USAGE_TOUCH, hapticFeedbackIntensity);
+            }
+
+            // A11y is not disabled by any haptic feedback setting.
+            mCurrentVibrationIntensities.put(USAGE_ACCESSIBILITY, positiveHapticFeedbackIntensity);
         }
         notifyListeners();
     }
 
-    /**
-     * Return the value to be used for {@link Settings.System#HARDWARE_HAPTIC_FEEDBACK_INTENSITY}
-     * when the value was not set by the user.
-     *
-     * <p>This should adapt the behavior preceding the introduction of this new setting key, which
-     * is to apply {@link Settings.System#HAPTIC_FEEDBACK_INTENSITY} unless it's disabled.
-     */
-    private int getHardwareFeedbackIntensityWhenSettingIsMissing(int hapticFeedbackIntensity) {
-        if (hapticFeedbackIntensity == Vibrator.VIBRATION_INTENSITY_OFF) {
-            return getDefaultIntensity(USAGE_HARDWARE_FEEDBACK);
-        }
-        return hapticFeedbackIntensity;
-    }
-
     @Override
     public String toString() {
         synchronized (mLock) {
+            StringBuilder vibrationIntensitiesString = new StringBuilder("{");
+            for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+                int usage = mCurrentVibrationIntensities.keyAt(i);
+                int intensity = mCurrentVibrationIntensities.valueAt(i);
+                vibrationIntensitiesString.append(VibrationAttributes.usageToString(usage))
+                        .append("=(").append(intensityToString(intensity))
+                        .append(",default:").append(intensityToString(getDefaultIntensity(usage)))
+                        .append("), ");
+            }
+            vibrationIntensitiesString.append('}');
             return "VibrationSettings{"
-                    + "mVibrateInputDevices=" + mVibrateInputDevices
-                    + ", mVibrateWhenRinging=" + mVibrateWhenRinging
-                    + ", mApplyRampingRinger=" + mApplyRampingRinger
+                    + "mVibratorConfig=" + mVibrationConfig
+                    + ", mVibrateInputDevices=" + mVibrateInputDevices
                     + ", mBatterySaverMode=" + mBatterySaverMode
                     + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
-                    + ", mHapticChannelMaxVibrationAmplitude="
-                    + getHapticChannelMaxVibrationAmplitude()
-                    + ", mRampStepDuration=" + mRampStepDuration
-                    + ", mRampDownDuration=" + mRampDownDuration
-                    + ", mHardwareHapticFeedbackIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
-                    + ", mHapticFeedbackIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_TOUCH))
-                    + ", mHapticFeedbackDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_TOUCH))
-                    + ", mNotificationIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
-                    + ", mNotificationDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
-                    + ", mRingIntensity="
-                    + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
-                    + ", mRingDefaultIntensity="
-                    + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+                    + ", mVibrationIntensities=" + vibrationIntensitiesString
                     + '}';
         }
     }
@@ -466,16 +444,28 @@
     /** Write current settings into given {@link ProtoOutputStream}. */
     public void dumpProto(ProtoOutputStream proto) {
         synchronized (mLock) {
+            proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY,
+                    getCurrentIntensity(USAGE_ALARM));
+            proto.write(VibratorManagerServiceDumpProto.ALARM_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_ALARM));
+            proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_INTENSITY,
+                    getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
+            proto.write(VibratorManagerServiceDumpProto.HARDWARE_FEEDBACK_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
             proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
-                    mHapticFeedbackIntensity);
+                    getCurrentIntensity(USAGE_TOUCH));
             proto.write(VibratorManagerServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_TOUCH));
+            proto.write(VibratorManagerServiceDumpProto.MEDIA_INTENSITY,
+                    getCurrentIntensity(USAGE_MEDIA));
+            proto.write(VibratorManagerServiceDumpProto.MEDIA_DEFAULT_INTENSITY,
+                    getDefaultIntensity(USAGE_MEDIA));
             proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_INTENSITY,
-                    mNotificationIntensity);
+                    getCurrentIntensity(USAGE_NOTIFICATION));
             proto.write(VibratorManagerServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_NOTIFICATION));
             proto.write(VibratorManagerServiceDumpProto.RING_INTENSITY,
-                    mRingIntensity);
+                    getCurrentIntensity(USAGE_RINGTONE));
             proto.write(VibratorManagerServiceDumpProto.RING_DEFAULT_INTENSITY,
                     getDefaultIntensity(USAGE_RINGTONE));
         }
@@ -506,13 +496,29 @@
         }
     }
 
-    private float getHapticChannelMaxVibrationAmplitude() {
-        synchronized (mLock) {
-            return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude();
+    @VibrationIntensity
+    private int toPositiveIntensity(int value, @VibrationIntensity int defaultValue) {
+        if (value == Vibrator.VIBRATION_INTENSITY_OFF) {
+            return defaultValue;
         }
+        return toIntensity(value, defaultValue);
     }
 
-    private int getSystemSetting(String settingName, int defaultValue) {
+    @VibrationIntensity
+    private int toIntensity(int value, @VibrationIntensity int defaultValue) {
+        if ((value < Vibrator.VIBRATION_INTENSITY_OFF)
+                || (value > Vibrator.VIBRATION_INTENSITY_HIGH)) {
+            return defaultValue;
+        }
+        return value;
+    }
+
+    private boolean loadBooleanSetting(String settingKey) {
+        return Settings.System.getIntForUser(mContext.getContentResolver(),
+                settingKey, 0, UserHandle.USER_CURRENT) != 0;
+    }
+
+    private int loadSystemSetting(String settingName, int defaultValue) {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 settingName, defaultValue, UserHandle.USER_CURRENT);
     }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 0396a11..8f703c5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -45,7 +45,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowTracing.WINSCOPE_EXT;
-import static com.android.server.wm.utils.RegionUtils.forEachRect;
 
 import android.accessibilityservice.AccessibilityTrace;
 import android.animation.ObjectAnimator;
@@ -101,6 +100,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow;
 import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
@@ -133,19 +133,22 @@
     private static final Rect EMPTY_RECT = new Rect();
     private static final float[] sTempFloats = new float[9];
 
-    private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
-    private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
+    private final SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
+    private final SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
             new SparseArray<>();
     private SparseArray<IBinder> mFocusedWindow = new SparseArray<>();
     private int mFocusedDisplay = -1;
     private boolean mIsImeVisible = false;
     // Set to true if initializing window population complete.
     private boolean mAllObserversInitialized = true;
+    private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator;
 
     AccessibilityController(WindowManagerService service) {
         mService = service;
         mAccessibilityTracing =
                 AccessibilityController.getAccessibilityControllerInternal(service);
+
+        mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this);
     }
 
     boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
@@ -209,7 +212,9 @@
                 }
                 mWindowsForAccessibilityObserver.remove(displayId);
             }
-            observer = new WindowsForAccessibilityObserver(mService, displayId, callback);
+            mAccessibilityWindowsPopulator.setWindowsNotification(true);
+            observer = new WindowsForAccessibilityObserver(mService, displayId, callback,
+                    mAccessibilityWindowsPopulator);
             mWindowsForAccessibilityObserver.put(displayId, observer);
             mAllObserversInitialized &= observer.mInitialized;
         } else {
@@ -224,6 +229,10 @@
                 }
             }
             mWindowsForAccessibilityObserver.remove(displayId);
+
+            if (mWindowsForAccessibilityObserver.size() <= 0) {
+                mAccessibilityWindowsPopulator.setWindowsNotification(false);
+            }
         }
     }
 
@@ -309,11 +318,6 @@
         if (displayMagnifier != null) {
             displayMagnifier.onDisplaySizeChanged(displayContent);
         }
-        final WindowsForAccessibilityObserver windowsForA11yObserver =
-                mWindowsForAccessibilityObserver.get(displayId);
-        if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindows();
-        }
     }
 
     void onAppWindowTransition(int displayId, int transition) {
@@ -341,11 +345,6 @@
         if (displayMagnifier != null) {
             displayMagnifier.onWindowTransition(windowState, transition);
         }
-        final WindowsForAccessibilityObserver windowsForA11yObserver =
-                mWindowsForAccessibilityObserver.get(displayId);
-        if (windowsForA11yObserver != null) {
-            windowsForA11yObserver.scheduleComputeChangedWindows();
-        }
     }
 
     void onWindowFocusChangedNot(int displayId) {
@@ -455,6 +454,19 @@
         return null;
     }
 
+    boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) {
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay",
+                    FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId);
+        }
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier == null) {
+            return false;
+        }
+
+        return displayMagnifier.getMagnificationSpec(outSpec);
+    }
+
     boolean hasCallbacks() {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -756,6 +768,25 @@
             return spec;
         }
 
+        boolean getMagnificationSpec(MagnificationSpec outSpec) {
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+                        FLAGS_MAGNIFICATION_CALLBACK);
+            }
+            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
+            if (spec == null) {
+                return false;
+            }
+
+            outSpec.setTo(spec);
+            if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+                mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+                        FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}");
+            }
+
+            return true;
+        }
+
         void getMagnificationRegion(Region outMagnificationRegion) {
             if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
                 mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
@@ -1403,20 +1434,18 @@
 
         private static final boolean DEBUG = false;
 
-        private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>();
+        private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>();
 
         private final Set<IBinder> mTempBinderSet = new ArraySet<>();
 
-        private final RectF mTempRectF = new RectF();
-
-        private final Matrix mTempMatrix = new Matrix();
-
         private final Point mTempPoint = new Point();
 
         private final Region mTempRegion = new Region();
 
         private final Region mTempRegion1 = new Region();
 
+        private final Region mTempRegion2 = new Region();
+
         private final WindowManagerService mService;
 
         private final Handler mHandler;
@@ -1431,10 +1460,11 @@
 
         // Set to true if initializing window population complete.
         private boolean mInitialized;
+        private final AccessibilityWindowsPopulator mA11yWindowsPopulator;
 
         WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
-                int displayId,
-                WindowsForAccessibilityCallback callback) {
+                int displayId, WindowsForAccessibilityCallback callback,
+                AccessibilityWindowsPopulator accessibilityWindowsPopulator) {
             mService = windowManagerService;
             mCallback = callback;
             mDisplayId = displayId;
@@ -1443,6 +1473,7 @@
                     AccessibilityController.getAccessibilityControllerInternal(mService);
             mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
                     .getSendRecurringAccessibilityEventsInterval();
+            mA11yWindowsPopulator = accessibilityWindowsPopulator;
             computeChangedWindows(true);
         }
 
@@ -1466,52 +1497,6 @@
             }
         }
 
-        boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
-            int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
-            int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
-                    true);
-            return shellLayer >= wsLayer;
-        }
-
-        int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots,
-                int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows,
-                Region unaccountedSpace, boolean focusedWindowAdded) {
-            while (shellRootIndex < shellRoots.size()
-                    && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) {
-                ShellRoot shellRoot = shellRoots.get(shellRootIndex);
-                shellRootIndex++;
-                final WindowInfo info = shellRoot.getWindowInfo();
-                if (info == null) {
-                    continue;
-                }
-
-                info.layer = addedWindows.size();
-                windows.add(info);
-                addedWindows.add(info.token);
-                unaccountedSpace.op(info.regionInScreen, unaccountedSpace,
-                        Region.Op.REVERSE_DIFFERENCE);
-                if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
-                    break;
-                }
-            }
-            return shellRootIndex;
-        }
-
-        private ArrayList<ShellRoot> getSortedShellRoots(
-                SparseArray<ShellRoot> originalShellRoots) {
-            ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size());
-            for (int i = originalShellRoots.size() - 1; i >= 0; --i) {
-                sortedShellRoots.add(originalShellRoots.valueAt(i));
-            }
-
-            sortedShellRoots.sort((left, right) ->
-                    mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true)
-                            - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(),
-                            true));
-
-            return sortedShellRoots;
-        }
-
         /**
          * Check if windows have changed, and send them to the accessibility subsystem if they have.
          *
@@ -1561,44 +1546,29 @@
                 Region unaccountedSpace = mTempRegion;
                 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
 
-                final SparseArray<WindowState> visibleWindows = mTempWindowStates;
-                populateVisibleWindowsOnScreen(visibleWindows);
+                final List<AccessibilityWindow> visibleWindows = mTempA11yWindows;
+                mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked(
+                        mDisplayId, visibleWindows);
                 Set<IBinder> addedWindows = mTempBinderSet;
                 addedWindows.clear();
 
                 boolean focusedWindowAdded = false;
 
                 final int visibleWindowCount = visibleWindows.size();
-                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>();
-
-                ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
 
                 // Iterate until we figure out what is touchable for the entire screen.
-                int shellRootIndex = 0;
-                for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                    final WindowState windowState = visibleWindows.valueAt(i);
-                    int prevShellRootIndex = shellRootIndex;
-                    shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex,
-                            windows, addedWindows, unaccountedSpace, focusedWindowAdded);
-
-                    // If a Shell Root was added, it could have accounted for all the space already.
-                    if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty()
-                            && focusedWindowAdded) {
-                        break;
-                    }
-
-                    final Region regionInScreen = new Region();
-                    computeWindowRegionInScreen(windowState, regionInScreen);
-                    if (windowMattersToAccessibility(windowState,
-                            regionInScreen, unaccountedSpace,
-                            skipRemainingWindowsForTaskFragments)) {
-                        addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows);
-                        if (windowMattersToUnaccountedSpaceComputation(windowState)) {
-                            updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace,
-                                    skipRemainingWindowsForTaskFragments);
+                for (int i = 0; i < visibleWindowCount; i++) {
+                    final AccessibilityWindow a11yWindow = visibleWindows.get(i);
+                    final Region regionInWindow = new Region();
+                    a11yWindow.getTouchableRegionInWindow(regionInWindow);
+                    if (windowMattersToAccessibility(a11yWindow, regionInWindow,
+                            unaccountedSpace)) {
+                        addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows);
+                        if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) {
+                            updateUnaccountedSpace(a11yWindow, unaccountedSpace);
                         }
-                        focusedWindowAdded |= windowState.isFocused();
-                    } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) {
+                        focusedWindowAdded |= a11yWindow.isFocused();
+                    } else if (a11yWindow.isUntouchableNavigationBar()) {
                         // If this widow is navigation bar without touchable region, accounting the
                         // region of navigation bar inset because all touch events from this region
                         // would be received by launcher, i.e. this region is a un-touchable one
@@ -1647,47 +1617,39 @@
 
         // Some windows should be excluded from unaccounted space computation, though they still
         // should be reported
-        private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) {
+        private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) {
             // Do not account space of trusted non-touchable windows, except the split-screen
             // divider.
             // If it's not trusted, touch events are not sent to the windows behind it.
-            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)
-                    && windowState.isTrustedOverlay()) {
+            if (((a11yWindow.getFlags() & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER)
+                    && a11yWindow.isTrustedOverlay()) {
                 return false;
             }
 
-            if (windowState.mAttrs.type
-                    == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+            if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
                 return false;
             }
             return true;
         }
 
-        private boolean windowMattersToAccessibility(WindowState windowState,
-                Region regionInScreen, Region unaccountedSpace,
-                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
-            final RecentsAnimationController controller = mService.getRecentsAnimationController();
-            if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) {
+        private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow,
+                Region regionInScreen, Region unaccountedSpace) {
+            if (a11yWindow.ignoreRecentsAnimationForAccessibility()) {
                 return false;
             }
 
-            if (windowState.isFocused()) {
+            if (a11yWindow.isFocused()) {
                 return true;
             }
 
-            // If the window is part of a task that we're finished with - ignore.
-            final TaskFragment taskFragment = windowState.getTaskFragment();
-            if (taskFragment != null
-                    && skipRemainingWindowsForTaskFragments.contains(taskFragment)) {
-                return false;
-            }
-
             // Ignore non-touchable windows, except the split-screen divider, which is
             // occasionally non-touchable but still useful for identifying split-screen
-            // mode.
-            if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
-                    && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
+            // mode and the PIP menu.
+            if (((a11yWindow.getFlags()
+                    & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+                    && (a11yWindow.getType() != TYPE_DOCK_DIVIDER
+                    && !a11yWindow.isPIPMenu())) {
                 return false;
             }
 
@@ -1697,88 +1659,36 @@
             }
 
             // Add windows of certain types not covered by modal windows.
-            if (isReportedWindowType(windowState.mAttrs.type)) {
+            if (isReportedWindowType(a11yWindow.getType())) {
                 return true;
             }
 
             return false;
         }
 
-        private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen,
-                Region unaccountedSpace,
-                ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) {
-            // Account for the space this window takes if the window
-            // is not an accessibility overlay which does not change
-            // the reported windows.
-            unaccountedSpace.op(regionInScreen, unaccountedSpace,
-                    Region.Op.REVERSE_DIFFERENCE);
-
-            // If a window is modal it prevents other windows from being touched
-            if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
-                if (!windowState.hasTapExcludeRegion()) {
-                    // Account for all space in the task, whether the windows in it are
-                    // touchable or not. The modal window blocks all touches from the task's
-                    // area.
-                    unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
-                            Region.Op.REVERSE_DIFFERENCE);
-                } else {
-                    // If a window has tap exclude region, we need to account it.
-                    final Region displayRegion = new Region(windowState.getDisplayFrame());
-                    final Region tapExcludeRegion = new Region();
-                    windowState.getTapExcludeRegion(tapExcludeRegion);
-                    displayRegion.op(tapExcludeRegion, displayRegion,
-                            Region.Op.REVERSE_DIFFERENCE);
-                    unaccountedSpace.op(displayRegion, unaccountedSpace,
-                            Region.Op.REVERSE_DIFFERENCE);
-                }
-
-                final TaskFragment taskFragment = windowState.getTaskFragment();
-                if (taskFragment != null) {
-                    // If the window is associated with a particular task, we can skip the
-                    // rest of the windows for that task.
-                    skipRemainingWindowsForTaskFragments.add(taskFragment);
-                } else if (!windowState.hasTapExcludeRegion()) {
-                    // If the window is not associated with a particular task, then it is
-                    // globally modal. In this case we can skip all remaining windows when
-                    // it doesn't has tap exclude region.
-                    unaccountedSpace.setEmpty();
-                }
-            }
-
-            // Account for the space of letterbox.
-            if (windowState.areAppWindowBoundsLetterboxed()) {
-                unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace,
+        private void updateUnaccountedSpace(AccessibilityWindow a11yWindow,
+                Region unaccountedSpace) {
+            if (a11yWindow.getType()
+                    != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+                // Account for the space this window takes if the window
+                // is not an accessibility overlay which does not change
+                // the reported windows.
+                final Region touchableRegion = mTempRegion2;
+                a11yWindow.getTouchableRegionInScreen(touchableRegion);
+                unaccountedSpace.op(touchableRegion, unaccountedSpace,
                         Region.Op.REVERSE_DIFFERENCE);
+                // Account for the space of letterbox.
+                final Region letterboxBounds = mTempRegion1;
+                if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) {
+                    unaccountedSpace.op(letterboxBounds,
+                            unaccountedSpace, Region.Op.REVERSE_DIFFERENCE);
+                }
             }
         }
 
-        private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) {
-            // Get the touchable frame.
-            Region touchableRegion = mTempRegion1;
-            windowState.getTouchableRegion(touchableRegion);
-
-            // Map the frame to get what appears on the screen.
-            Matrix matrix = mTempMatrix;
-            populateTransformationMatrix(windowState, matrix);
-
-            forEachRect(touchableRegion, rect -> {
-                // Move to origin as all transforms are captured by the matrix.
-                RectF windowFrame = mTempRectF;
-                windowFrame.set(rect);
-                windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top);
-
-                matrix.mapRect(windowFrame);
-
-                // Union all rects.
-                outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
-                        (int) windowFrame.right, (int) windowFrame.bottom));
-            });
-        }
-
-        private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen,
-                List<WindowInfo> out, Set<IBinder> tokenOut) {
-            final WindowInfo window = windowState.getWindowInfo();
+        private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
+                Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) {
+            final WindowInfo window = a11yWindow.getWindowInfo();
             window.regionInScreen.set(regionInScreen);
             window.layer = tokenOut.size();
             out.add(window);
@@ -1805,23 +1715,6 @@
                     && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
         }
 
-        private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
-            final List<WindowState> tempWindowStatesList = new ArrayList<>();
-            final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
-            if (dc == null) {
-                return;
-            }
-
-            dc.forAllWindows(w -> {
-                if (w.isVisible()) {
-                    tempWindowStatesList.add(w);
-                }
-            }, false /* traverseTopToBottom */);
-            for (int i = 0; i < tempWindowStatesList.size(); i++) {
-                outWindows.put(i, tempWindowStatesList.get(i));
-            }
-        }
-
         private WindowState getTopFocusWindow() {
             return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus;
         }
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
new file mode 100644
index 0000000..f31ae06
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+
+import static com.android.server.wm.utils.RegionUtils.forEachRect;
+
+import android.annotation.NonNull;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.IWindow;
+import android.view.InputWindowHandle;
+import android.view.MagnificationSpec;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.window.WindowInfosListener;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is the accessibility windows population adapter.
+ */
+public final class AccessibilityWindowsPopulator extends WindowInfosListener {
+
+    private static final String TAG = AccessibilityWindowsPopulator.class.getSimpleName();
+    // If the surface flinger callback is not coming within in 2 frames time, i.e. about
+    // 35ms, then assuming the windows become stable.
+    private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35;
+    // To avoid the surface flinger callbacks always comes within in 2 frames, then no windows
+    // are reported to the A11y framework, and the animation duration time is 500ms, so setting
+    // this value as the max timeout value to force computing changed windows.
+    private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500;
+
+    private static final float[] sTempFloats = new float[9];
+
+    private final WindowManagerService mService;
+    private final AccessibilityController mAccessibilityController;
+    @GuardedBy("mLock")
+    private final SparseArray<List<InputWindowHandle>> mInputWindowHandlesOnDisplays =
+            new SparseArray<>();
+    @GuardedBy("mLock")
+    private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>();
+    @GuardedBy("mLock")
+    private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>();
+    @GuardedBy("mLock")
+    private boolean mWindowsNotificationEnabled = false;
+    private final Object mLock = new Object();
+    private final Handler mHandler;
+
+    AccessibilityWindowsPopulator(WindowManagerService service,
+            AccessibilityController accessibilityController) {
+        mService = service;
+        mAccessibilityController = accessibilityController;
+        mHandler = new MyHandler(mService.mH.getLooper());
+
+        register();
+    }
+
+    /**
+     * Gets the visible windows list with the window layer on the specified display.
+     *
+     * @param displayId The display.
+     * @param outWindows The visible windows list. The z-order of each window in the list
+     *                   is from the top to bottom.
+     */
+    public void populateVisibleWindowsOnScreenLocked(int displayId,
+            List<AccessibilityWindow> outWindows) {
+        List<InputWindowHandle> inputWindowHandles;
+        final Matrix inverseMatrix = new Matrix();
+        final Matrix displayMatrix = new Matrix();
+
+        synchronized (mLock) {
+            inputWindowHandles = mInputWindowHandlesOnDisplays.get(displayId);
+            if (inputWindowHandles == null) {
+                outWindows.clear();
+
+                return;
+            }
+            inverseMatrix.set(mMagnificationSpecInverseMatrix.get(displayId));
+
+            final DisplayInfo displayInfo = mDisplayInfos.get(displayId);
+            if (displayInfo != null) {
+                displayMatrix.set(displayInfo.mTransform);
+            } else {
+                Slog.w(TAG, "The displayInfo of this displayId (" + displayId + ") called "
+                        + "back from the surface fligner is null");
+            }
+        }
+
+        final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+        final ShellRoot shellroot = dc.mShellRoots.get(WindowManager.SHELL_ROOT_LAYER_PIP);
+        final IBinder pipMenuIBinder =
+                shellroot != null ? shellroot.getAccessibilityWindowToken() : null;
+
+        for (final InputWindowHandle windowHandle : inputWindowHandles) {
+            final AccessibilityWindow accessibilityWindow =
+                    AccessibilityWindow.initializeData(mService, windowHandle, inverseMatrix,
+                            pipMenuIBinder, displayMatrix);
+
+            outWindows.add(accessibilityWindow);
+        }
+    }
+
+    @Override
+    public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
+            DisplayInfo[] displayInfos) {
+        synchronized (mLock) {
+            mVisibleWindows.clear();
+            for (InputWindowHandle window : windowHandles) {
+                if (window.visible && window.getWindow() != null) {
+                    mVisibleWindows.add(window);
+                }
+            }
+
+            mDisplayInfos.clear();
+            for (final DisplayInfo displayInfo : displayInfos) {
+                mDisplayInfos.put(displayInfo.mDisplayId, displayInfo);
+            }
+
+            if (mWindowsNotificationEnabled) {
+                if (!mHandler.hasMessages(
+                        MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
+                    mHandler.sendEmptyMessageDelayed(
+                            MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
+                            WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
+                }
+                populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
+            }
+        }
+    }
+
+    /**
+     * Sets to notify the accessibilityController to compute changed windows on
+     * the display after populating the visible windows if the windows reported
+     * from the surface flinger changes.
+     *
+     * @param register {@code true} means starting windows population.
+     */
+    public void setWindowsNotification(boolean register) {
+        synchronized (mLock) {
+            if (mWindowsNotificationEnabled == register) {
+                return;
+            }
+            mWindowsNotificationEnabled = register;
+            if (mWindowsNotificationEnabled) {
+                populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
+            } else {
+                releaseResources();
+            }
+        }
+    }
+
+    private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() {
+        final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>();
+
+        for (final InputWindowHandle windowHandle : mVisibleWindows) {
+            List<InputWindowHandle> inputWindowHandles = tempWindowHandleList.get(
+                    windowHandle.displayId);
+
+            if (inputWindowHandles == null) {
+                inputWindowHandles = new ArrayList<>();
+                tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles);
+                generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId);
+            }
+            inputWindowHandles.add(windowHandle);
+        }
+
+        final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
+
+        getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList,
+                mInputWindowHandlesOnDisplays);
+        // Clones all windows from the callback of the surface flinger.
+        mInputWindowHandlesOnDisplays.clear();
+        for (int i = 0; i < tempWindowHandleList.size(); i++) {
+            final int displayId = tempWindowHandleList.keyAt(i);
+            mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId));
+        }
+
+        if (displayIdsForWindowsChanged.size() > 0) {
+            if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) {
+                mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
+                        displayIdsForWindowsChanged).sendToTarget();
+            }
+
+            return;
+        }
+        mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
+        mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE,
+                SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS);
+    }
+
+    private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged,
+            SparseArray<List<InputWindowHandle>> newWindowsList,
+            SparseArray<List<InputWindowHandle>> oldWindowsList) {
+        for (int i = 0; i < newWindowsList.size(); i++) {
+            final int displayId = newWindowsList.keyAt(i);
+            final List<InputWindowHandle> newWindows = newWindowsList.get(displayId);
+            final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId);
+
+            if (hasWindowsChangedLocked(newWindows, oldWindows)) {
+                outDisplayIdsForWindowsChanged.add(displayId);
+            }
+        }
+    }
+
+    private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows,
+            List<InputWindowHandle> oldWindows) {
+        if (oldWindows == null || oldWindows.size() != newWindows.size()) {
+            return true;
+        }
+
+        final int windowsCount = newWindows.size();
+        // Since we always traverse windows from high to low layer,
+        // the old and new windows at the same index should be the
+        // same, otherwise something changed.
+        for (int i = 0; i < windowsCount; i++) {
+            final InputWindowHandle newWindow = newWindows.get(i);
+            final InputWindowHandle oldWindow = oldWindows.get(i);
+
+            if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void generateMagnificationSpecInverseMatrixLocked(int displayId) {
+        MagnificationSpec spec = new MagnificationSpec();
+        if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) {
+            return;
+        }
+        sTempFloats[Matrix.MSCALE_X] = spec.scale;
+        sTempFloats[Matrix.MSKEW_Y] = 0;
+        sTempFloats[Matrix.MSKEW_X] = 0;
+        sTempFloats[Matrix.MSCALE_Y] = spec.scale;
+        sTempFloats[Matrix.MTRANS_X] = spec.offsetX;
+        sTempFloats[Matrix.MTRANS_Y] = spec.offsetY;
+        sTempFloats[Matrix.MPERSP_0] = 0;
+        sTempFloats[Matrix.MPERSP_1] = 0;
+        sTempFloats[Matrix.MPERSP_2] = 1;
+
+        final Matrix tempMatrix = new Matrix();
+        tempMatrix.setValues(sTempFloats);
+
+        final Matrix inverseMatrix = new Matrix();
+        final boolean result = tempMatrix.invert(inverseMatrix);
+
+        if (!result) {
+            Slog.e(TAG, "Can't inverse the magnification spec matrix with the "
+                    + "magnification spec = " + spec + " on the displayId = " + displayId);
+            return;
+        }
+        mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix);
+    }
+
+    private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) {
+        mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT);
+
+        for (int i = 0; i < displayIdsForWindowsChanged.size(); i++) {
+            mAccessibilityController.performComputeChangedWindowsNot(
+                    displayIdsForWindowsChanged.get(i), false);
+        }
+    }
+
+    private void forceUpdateWindows() {
+        final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
+
+        synchronized (mLock) {
+            for (int i = 0; i < mInputWindowHandlesOnDisplays.size(); i++) {
+                final int displayId = mInputWindowHandlesOnDisplays.keyAt(i);
+                displayIdsForWindowsChanged.add(displayId);
+            }
+        }
+        notifyWindowsChanged(displayIdsForWindowsChanged);
+    }
+
+    @GuardedBy("mLock")
+    private void releaseResources() {
+        mInputWindowHandlesOnDisplays.clear();
+        mMagnificationSpecInverseMatrix.clear();
+        mVisibleWindows.clear();
+        mDisplayInfos.clear();
+        mWindowsNotificationEnabled = false;
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    private class MyHandler extends Handler {
+        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
+        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE = 2;
+        public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT = 3;
+
+        MyHandler(Looper looper) {
+            super(looper, null, false);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
+                    final List<Integer> displayIdsForWindowsChanged = (List<Integer>) message.obj;
+                    notifyWindowsChanged(displayIdsForWindowsChanged);
+                } break;
+
+                case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE: {
+                    forceUpdateWindows();
+                } break;
+
+                case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT: {
+                    Slog.w(TAG, "Windows change within in 2 frames continuously over 500 ms "
+                            + "and notify windows changed immediately");
+                    mHandler.removeMessages(
+                            MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE);
+
+                    forceUpdateWindows();
+                } break;
+            }
+        }
+    }
+
+    /**
+     * This class represents information about a window from the
+     * surface flinger to the accessibility framework.
+     */
+    public static class AccessibilityWindow {
+        private static final Region TEMP_REGION = new Region();
+        private static final RectF TEMP_RECTF = new RectF();
+        // Data
+        private IWindow mWindow;
+        private int mDisplayId;
+        private int mFlags;
+        private int mType;
+        private int mPrivateFlags;
+        private boolean mIsPIPMenu;
+        private boolean mIsFocused;
+        private boolean mShouldMagnify;
+        private boolean mIgnoreDuetoRecentsAnimation;
+        private boolean mIsTrustedOverlay;
+        private final Region mTouchableRegionInScreen = new Region();
+        private final Region mTouchableRegionInWindow = new Region();
+        private final Region mLetterBoxBounds = new Region();
+        private WindowInfo mWindowInfo;
+
+        /**
+         * Returns the instance after initializing the internal data.
+         * @param service The window manager service.
+         * @param inputWindowHandle The window from the surface flinger.
+         * @param inverseMatrix The magnification spec inverse matrix.
+         */
+        public static AccessibilityWindow initializeData(WindowManagerService service,
+                InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder,
+                Matrix displayMatrix) {
+            final IWindow window = inputWindowHandle.getWindow();
+            final WindowState windowState = window != null ? service.mWindowMap.get(
+                    window.asBinder()) : null;
+
+            final AccessibilityWindow instance = new AccessibilityWindow();
+
+            instance.mWindow = inputWindowHandle.getWindow();
+            instance.mDisplayId = inputWindowHandle.displayId;
+            instance.mFlags = inputWindowHandle.layoutParamsFlags;
+            instance.mType = inputWindowHandle.layoutParamsType;
+            instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder);
+
+            // TODO (b/199357848): gets the private flag of the window from other way.
+            instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0;
+            // TODO (b/199358208) : using new way to implement the focused window.
+            instance.mIsFocused = windowState != null && windowState.isFocused();
+            instance.mShouldMagnify = windowState == null || windowState.shouldMagnify();
+
+            final RecentsAnimationController controller = service.getRecentsAnimationController();
+            instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
+                    && controller.shouldIgnoreForAccessibility(windowState);
+            instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay;
+
+            // TODO (b/199358388) : gets the letterbox bounds of the window from other way.
+            if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) {
+                getLetterBoxBounds(windowState, instance.mLetterBoxBounds);
+            }
+
+            final Rect windowFrame = new Rect(inputWindowHandle.frameLeft,
+                    inputWindowHandle.frameTop, inputWindowHandle.frameRight,
+                    inputWindowHandle.frameBottom);
+            getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion,
+                    instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix);
+            getUnMagnifiedTouchableRegion(instance.mShouldMagnify,
+                    inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen,
+                    inverseMatrix, displayMatrix);
+            instance.mWindowInfo = windowState != null
+                    ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance);
+
+            return instance;
+        }
+
+        /**
+         * Returns the touchable region in the screen.
+         * @param outRegion The touchable region.
+         */
+        public void getTouchableRegionInScreen(Region outRegion) {
+            outRegion.set(mTouchableRegionInScreen);
+        }
+
+        /**
+         * Returns the touchable region in the window.
+         * @param outRegion The touchable region.
+         */
+        public void getTouchableRegionInWindow(Region outRegion) {
+            outRegion.set(mTouchableRegionInWindow);
+        }
+
+        /**
+         * @return the layout parameter flag {@link android.view.WindowManager.LayoutParams#flags}.
+         */
+        public int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * @return the layout parameter type {@link android.view.WindowManager.LayoutParams#type}.
+         */
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * @return the layout parameter private flag
+         * {@link android.view.WindowManager.LayoutParams#privateFlags}.
+         */
+        public int getPrivateFlag() {
+            return mPrivateFlags;
+        }
+
+        /**
+         * @return the windowInfo {@link WindowInfo}.
+         */
+        public WindowInfo getWindowInfo() {
+            return mWindowInfo;
+        }
+
+        /**
+         * Gets the letter box bounds if activity bounds are letterboxed
+         * or letterboxed for display cutout.
+         *
+         * @return {@code true} there's a letter box bounds.
+         */
+        public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) {
+            if (mLetterBoxBounds.isEmpty()) {
+                return false;
+            }
+
+            outBounds.set(mLetterBoxBounds);
+            return true;
+        }
+
+        /**
+         * @return true if this window should be magnified.
+         */
+        public boolean shouldMagnify() {
+            return mShouldMagnify;
+        }
+
+        /**
+         * @return true if this window is focused.
+         */
+        public boolean isFocused() {
+            return mIsFocused;
+        }
+
+        /**
+         * @return true if it's running the recent animation but not the target app.
+         */
+        public boolean ignoreRecentsAnimationForAccessibility() {
+            return mIgnoreDuetoRecentsAnimation;
+        }
+
+        /**
+         * @return true if this window is the trusted overlay.
+         */
+        public boolean isTrustedOverlay() {
+            return mIsTrustedOverlay;
+        }
+
+        /**
+         * @return true if this window is the navigation bar with the gesture mode.
+         */
+        public boolean isUntouchableNavigationBar() {
+            if (mType != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) {
+                return false;
+            }
+
+            return mTouchableRegionInScreen.isEmpty();
+        }
+
+        /**
+         * @return true if this window is PIP menu.
+         */
+        public boolean isPIPMenu() {
+            return mIsPIPMenu;
+        }
+
+        private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion,
+                Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) {
+            // Some modal windows, like the activity with Theme.dialog, has the full screen
+            // as its touchable region, but its window frame is smaller than the touchable
+            // region. The region we report should be the touchable area in the window frame
+            // for the consistency and match developers expectation.
+            // So we need to make the intersection between the frame and touchable region to
+            // obtain the real touch region in the screen.
+            Region touchRegion = TEMP_REGION;
+            touchRegion.set(inRegion);
+            touchRegion.op(frame, Region.Op.INTERSECT);
+
+            getUnMagnifiedTouchableRegion(shouldMagnify, touchRegion, outRegion, inverseMatrix,
+                    displayMatrix);
+        }
+
+        /**
+         * Gets the un-magnified touchable region. If this window can be magnified and magnifying,
+         * we will transform the input touchable region by applying the inverse matrix of the
+         * magnification spec to get the un-magnified touchable region.
+         * @param shouldMagnify The window can be magnified.
+         * @param inRegion The touchable region of this window.
+         * @param outRegion The un-magnified touchable region of this window.
+         * @param inverseMatrix The inverse matrix of the magnification spec.
+         * @param displayMatrix The display transform matrix which takes display coordinates to
+         *                      logical display coordinates.
+         */
+        private static void getUnMagnifiedTouchableRegion(boolean shouldMagnify, Region inRegion,
+                Region outRegion, Matrix inverseMatrix, Matrix displayMatrix) {
+            if ((!shouldMagnify || inverseMatrix.isIdentity()) && displayMatrix.isIdentity()) {
+                outRegion.set(inRegion);
+                return;
+            }
+
+            forEachRect(inRegion, rect -> {
+                // Move to origin as all transforms are captured by the matrix.
+                RectF windowFrame = TEMP_RECTF;
+                windowFrame.set(rect);
+
+                inverseMatrix.mapRect(windowFrame);
+                displayMatrix.mapRect(windowFrame);
+                // Union all rects.
+                outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top,
+                        (int) windowFrame.right, (int) windowFrame.bottom));
+            });
+        }
+
+        private static WindowInfo getWindowInfoForWindowlessWindows(AccessibilityWindow window) {
+            WindowInfo windowInfo = WindowInfo.obtain();
+            windowInfo.displayId = window.mDisplayId;
+            windowInfo.type = window.mType;
+            windowInfo.token = window.mWindow.asBinder();
+            windowInfo.hasFlagWatchOutsideTouch = (window.mFlags
+                    & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0;
+            windowInfo.inPictureInPicture = false;
+
+            // There only are two windowless windows now, one is split window, and the other
+            // one is PIP.
+            if (windowInfo.type == TYPE_DOCK_DIVIDER) {
+                windowInfo.title = "Splitscreen Divider";
+            } else if (window.mIsPIPMenu) {
+                windowInfo.title = "Picture-in-Picture menu";
+            }
+            return windowInfo;
+        }
+
+        private static void getLetterBoxBounds(WindowState windowState, Region outRegion) {
+            final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets();
+            final Rect nonLetterboxRect = windowState.getBounds();
+
+            nonLetterboxRect.inset(letterboxInsets);
+            outRegion.set(windowState.getBounds());
+            outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE);
+        }
+
+        @Override
+        public String toString() {
+            String builder = "A11yWindow=[" + mWindow.asBinder()
+                    + ", displayId=" + mDisplayId
+                    + ", flag=0x" + Integer.toHexString(mFlags)
+                    + ", type=" + mType
+                    + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags)
+                    + ", focused=" + mIsFocused
+                    + ", shouldMagnify=" + mShouldMagnify
+                    + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation
+                    + ", isTrustedOverlay=" + mIsTrustedOverlay
+                    + ", regionInScreen=" + mTouchableRegionInScreen
+                    + ", touchableRegion=" + mTouchableRegionInWindow
+                    + ", letterBoxBounds=" + mLetterBoxBounds
+                    + ", isPIPMenu=" + mIsPIPMenu
+                    + ", windowInfo=" + mWindowInfo
+                    + "]";
+
+            return builder;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1bafd49..ddd624d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -437,9 +437,9 @@
     private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT =
             START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * MINUTE_IN_MILLIS;
 
-    // Activity tokens of system activities that are delegating their call to
-    // #startActivityByCaller, keyed by the permissionToken granted to the delegate.
-    final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>();
+    // The component name of the delegated activities that are allowed to call
+    // #startActivityAsCaller with the one-time used permission token.
+    final HashMap<IBinder, ComponentName> mStartActivitySources = new HashMap<>();
 
     // Permission tokens that have expired, but we remember for error reporting.
     final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>();
@@ -1513,7 +1513,7 @@
     }
 
     @Override
-    public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
+    public IBinder requestStartActivityPermissionToken(ComponentName componentName) {
         int callingUid = Binder.getCallingUid();
         if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
             throw new SecurityException("Only the system process can request a permission token, "
@@ -1521,7 +1521,7 @@
         }
         IBinder permissionToken = new Binder();
         synchronized (mGlobalLock) {
-            mStartActivitySources.put(permissionToken, delegatorToken);
+            mStartActivitySources.put(permissionToken, componentName);
         }
 
         Message expireMsg = PooledLambda.obtainMessage(
@@ -1546,7 +1546,7 @@
         // 1)  The caller is an activity that is part of the core framework, and then only when it
         //     is running as the system.
         // 2)  The caller provides a valid permissionToken.  Permission tokens are one-time use and
-        //     can only be requested by a system activity, which may then delegate this call to
+        //     can only be requested from system uid, which may then delegate this call to
         //     another app.
         final ActivityRecord sourceRecord;
         final int targetUid;
@@ -1557,18 +1557,26 @@
             if (resultTo == null) {
                 throw new SecurityException("Must be called from an activity");
             }
-            final IBinder sourceToken;
+
+            sourceRecord = ActivityRecord.isInAnyTask(resultTo);
+            if (sourceRecord == null) {
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (sourceRecord.app == null) {
+                throw new SecurityException("Called without a process attached to activity");
+            }
+
+            final ComponentName componentName;
             if (permissionToken != null) {
                 // To even attempt to use a permissionToken, an app must also have this signature
                 // permission.
                 mAmInternal.enforceCallingPermission(
                         android.Manifest.permission.START_ACTIVITY_AS_CALLER,
                         "startActivityAsCaller");
-                // If called with a permissionToken, we want the sourceRecord from the delegator
-                // activity that requested this token.
-                sourceToken = mStartActivitySources.remove(permissionToken);
-                if (sourceToken == null) {
-                    // Invalid permissionToken, check if it recently expired.
+                // If called with a permissionToken, the caller must be the same component that
+                // was allowed to use the permissionToken.
+                componentName = mStartActivitySources.remove(permissionToken);
+                if (!sourceRecord.mActivityComponent.equals(componentName)) {
                     if (mExpiredStartAsCallerTokens.contains(permissionToken)) {
                         throw new SecurityException("Called with expired permission token: "
                                 + permissionToken);
@@ -1578,33 +1586,21 @@
                     }
                 }
             } else {
-                // This method was called directly by the source.
-                sourceToken = resultTo;
-            }
-
-            sourceRecord = ActivityRecord.isInAnyTask(sourceToken);
-            if (sourceRecord == null) {
-                throw new SecurityException("Called with bad activity token: " + sourceToken);
-            }
-            if (sourceRecord.app == null) {
-                throw new SecurityException("Called without a process attached to activity");
-            }
-
-            // Whether called directly or from a delegate, the source activity must be from the
-            // android package.
-            if (!sourceRecord.info.packageName.equals("android")) {
-                throw new SecurityException("Must be called from an activity that is "
-                        + "declared in the android package");
-            }
-
-            if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
-                // This is still okay, as long as this activity is running under the
-                // uid of the original calling activity.
-                if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
-                    throw new SecurityException(
-                            "Calling activity in uid " + sourceRecord.app.mUid
-                                    + " must be system uid or original calling uid "
-                                    + sourceRecord.launchedFromUid);
+                // Whether called directly or from a delegate, the source activity must be from the
+                // android package.
+                if (!sourceRecord.info.packageName.equals("android")) {
+                    throw new SecurityException("Must be called from an activity that is "
+                            + "declared in the android package");
+                }
+                if (UserHandle.getAppId(sourceRecord.app.mUid) != SYSTEM_UID) {
+                    // This is still okay, as long as this activity is running under the
+                    // uid of the original calling activity.
+                    if (sourceRecord.app.mUid != sourceRecord.launchedFromUid) {
+                        throw new SecurityException(
+                                "Calling activity in uid " + sourceRecord.app.mUid
+                                        + " must be system uid or original calling uid "
+                                        + sourceRecord.launchedFromUid);
+                    }
                 }
             }
             if (ignoreTargetSecurity) {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 47622bc..9661e8d 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -24,13 +24,11 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
 import static android.window.DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT;
 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
 
 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
@@ -134,11 +132,6 @@
                         .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
                                 TYPE_NOTIFICATION_SHADE)
                         .build())
-                        .addFeature(new Feature.Builder(wmService.mPolicy,
-                                "OneHandedBackgroundPanel",
-                                FEATURE_ONE_HANDED_BACKGROUND_PANEL)
-                                .upTo(TYPE_WALLPAPER)
-                                .build())
                         .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
                                 FEATURE_ONE_HANDED)
                                 .all()
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 3f2e975..5a420ca 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -983,29 +983,6 @@
             mWmService.checkDrawnWindowsLocked();
         }
 
-        final int N = mWmService.mPendingRemove.size();
-        if (N > 0) {
-            if (mWmService.mPendingRemoveTmp.length < N) {
-                mWmService.mPendingRemoveTmp = new WindowState[N + 10];
-            }
-            mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
-            mWmService.mPendingRemove.clear();
-            ArrayList<DisplayContent> displayList = new ArrayList();
-            for (i = 0; i < N; i++) {
-                final WindowState w = mWmService.mPendingRemoveTmp[i];
-                w.removeImmediately();
-                final DisplayContent displayContent = w.getDisplayContent();
-                if (displayContent != null && !displayList.contains(displayContent)) {
-                    displayList.add(displayContent);
-                }
-            }
-
-            for (int j = displayList.size() - 1; j >= 0; --j) {
-                final DisplayContent dc = displayList.get(j);
-                dc.assignWindowLayers(true /*setLayoutNeeded*/);
-            }
-        }
-
         forAllDisplays(dc -> {
             dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
             dc.updateSystemGestureExclusion();
@@ -2052,24 +2029,29 @@
         try {
             final Task task = r.getTask();
 
-            // If the activity in current PIP task needs to be moved back to the parent Task of next
-            // PIP activity, we can't use that parent Task as the next PIP Task.
-            // Because we need to start the Shell transition from the root Task, we delay to dismiss
-            // the current PIP task until root Task is ready.
-            boolean origPipWillBeMovedToTask = false;
+            // Create a transition now to collect the current pinned Task dismiss. Only do the
+            // create here as the Task (trigger) to enter PIP is not ready yet.
+            final TransitionController transitionController = task.mTransitionController;
+            Transition newTransition = null;
+            if (transitionController.isCollecting()) {
+                transitionController.setReady(task, false /* ready */);
+            } else if (transitionController.getTransitionPlayer() != null) {
+                newTransition = transitionController.createTransition(TRANSIT_PIP);
+            }
+
+            // This will change the root pinned task's windowing mode to its original mode, ensuring
+            // we only have one root task that is in pinned mode.
             final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask();
             if (rootPinnedTask != null) {
-                final ActivityRecord topPipActivity = rootPinnedTask.getTopMostActivity();
-                if (topPipActivity != null && topPipActivity.getLastParentBeforePip() == task) {
-                    origPipWillBeMovedToTask = true;
-                }
+                transitionController.collect(rootPinnedTask);
+                rootPinnedTask.dismissPip();
             }
 
             // Set a transition to ensure that we don't immediately try and update the visibility
             // of the activity entering PIP
             r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
 
-            final boolean singleActivity = task.getChildCount() == 1 && !origPipWillBeMovedToTask;
+            final boolean singleActivity = task.getChildCount() == 1;
             final Task rootTask;
             if (singleActivity) {
                 rootTask = task;
@@ -2123,7 +2105,7 @@
                 final ActivityRecord oldTopActivity = task.getTopMostActivity();
                 if (oldTopActivity != null && oldTopActivity.isState(STOPPED)
                         && task.getDisplayContent().mAppTransition.containsTransitRequest(
-                        TRANSIT_TO_BACK) && !origPipWillBeMovedToTask) {
+                        TRANSIT_TO_BACK)) {
                     task.getDisplayContent().mClosingApps.add(oldTopActivity);
                     oldTopActivity.mRequestForceTransition = true;
                 }
@@ -2137,14 +2119,13 @@
                 // display area, so reparent.
                 rootTask.reparent(taskDisplayArea, true /* onTop */);
             }
-            rootTask.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, rootTask);
 
-            // This will change the root pinned task's windowing mode to its original mode, ensuring
-            // we only have one root task that is in pinned mode.
-            if (rootPinnedTask != null) {
-                rootTask.mTransitionController.collect(rootPinnedTask);
-                rootPinnedTask.dismissPip();
+            // The new PIP Task is ready, start the transition before updating the windowing mode.
+            if (newTransition != null) {
+                transitionController.requestStartTransition(newTransition, rootTask,
+                        null /* remoteTransition */, null /* displayChange */);
             }
+            transitionController.collect(rootTask);
 
             // Defer the windowing mode change until after the transition to prevent the activity
             // from doing work and changing the activity visuals while animating
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 6ed59e9..f9d7b53 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -25,15 +25,14 @@
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.IWindow;
 import android.view.SurfaceControl;
-import android.view.WindowInfo;
 import android.view.WindowManager;
 import android.view.animation.Animation;
 
@@ -136,47 +135,12 @@
                 ANIMATION_TYPE_WINDOW_ANIMATION);
     }
 
-    WindowInfo getWindowInfo() {
-        if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER
-                && mShellRootLayer != SHELL_ROOT_LAYER_PIP) {
-            return null;
+    @Nullable
+    IBinder getAccessibilityWindowToken() {
+        if (mAccessibilityWindow != null) {
+            return mAccessibilityWindow.asBinder();
         }
-        if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER
-                && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
-            return null;
-        }
-        if (mShellRootLayer == SHELL_ROOT_LAYER_PIP
-                && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) {
-            return null;
-        }
-        if (mAccessibilityWindow == null) {
-            return null;
-        }
-        WindowInfo windowInfo = WindowInfo.obtain();
-        windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
-        windowInfo.type = mToken.windowType;
-        windowInfo.layer = mToken.getWindowLayerFromType();
-        windowInfo.token = mAccessibilityWindow.asBinder();
-        windowInfo.focused = false;
-        windowInfo.hasFlagWatchOutsideTouch = false;
-        final Rect regionRect = new Rect();
-
-
-        // DividerView
-        if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) {
-            windowInfo.inPictureInPicture = false;
-            mDisplayContent.getDockedDividerController().getTouchRegion(regionRect);
-            windowInfo.regionInScreen.set(regionRect);
-            windowInfo.title = "Splitscreen Divider";
-        }
-        // PipMenuView
-        if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) {
-            windowInfo.inPictureInPicture = true;
-            mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect);
-            windowInfo.regionInScreen.set(regionRect);
-            windowInfo.title = "Picture-in-Picture menu";
-        }
-        return windowInfo;
+        return null;
     }
 
     void setAccessibilityWindow(IWindow window) {
@@ -197,9 +161,5 @@
                 mAccessibilityWindow = null;
             }
         }
-        if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
-            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
-                    mDisplayContent.getDisplayId());
-        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index fe405e5..fc154a8 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -107,7 +107,7 @@
     /** Returns {@code true} if visibility is changed. */
     boolean updateWallpaperWindows(boolean visible) {
         boolean changed = false;
-        if (isVisible() != visible) {
+        if (mVisibleRequested != visible) {
             ProtoLog.d(WM_DEBUG_WALLPAPER, "Wallpaper token %s visible=%b",
                     token, visible);
             setVisibility(visible);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7be128b..b5e6f49 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -222,7 +222,6 @@
 import android.util.EventLog;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.TypedValue;
@@ -588,20 +587,6 @@
     final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
 
     /**
-     * Windows whose animations have ended and now must be removed.
-     */
-    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
-
-    /**
-     * Used when processing mPendingRemove to avoid working on the original array.
-     */
-    WindowState[] mPendingRemoveTmp = new WindowState[20];
-
-    // TODO: use WindowProcessController once go/wm-unified is done.
-    /** Mapping of process pids to configurations */
-    final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>();
-
-    /**
      * Windows whose surface should be destroyed.
      */
     final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
@@ -2038,7 +2023,6 @@
             dc.mWinRemovedSinceNullFocus.add(win);
         }
         mEmbeddedWindowController.onWindowRemoved(win);
-        mPendingRemove.remove(win);
         mResizingWindows.remove(win);
         updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
         mWindowsChanged = true;
@@ -6342,23 +6326,6 @@
                 }
             }
         }
-        if (mPendingRemove.size() > 0) {
-            pw.println();
-            pw.println("  Remove pending for:");
-            for (int i=mPendingRemove.size()-1; i>=0; i--) {
-                WindowState w = mPendingRemove.get(i);
-                if (windows == null || windows.contains(w)) {
-                    pw.print("  Remove #"); pw.print(i); pw.print(' ');
-                            pw.print(w);
-                    if (dumpAll) {
-                        pw.println(":");
-                        w.dump(pw, "    ", true);
-                    } else {
-                        pw.println();
-                    }
-                }
-            }
-        }
         if (mForceRemoves != null && mForceRemoves.size() > 0) {
             pw.println();
             pw.println("  Windows force removing:");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 573ff2f..5bbe2cd 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4809,9 +4809,6 @@
         if (isAnimating()) {
             return;
         }
-        if (mWmService.mAccessibilityController.hasCallbacks()) {
-            mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
-        }
 
         if (!isSelfOrAncestorWindowAnimatingExit()) {
             return;
@@ -4838,15 +4835,20 @@
             if (hasSurface) {
                 mWmService.mDestroySurface.add(this);
             }
-            if (mRemoveOnExit) {
-                mWmService.mPendingRemove.add(this);
-                mRemoveOnExit = false;
-            }
         }
         mAnimatingExit = false;
         getDisplayContent().mWallpaperController.hideWallpapers(this);
     }
 
+    @Override
+    boolean handleCompleteDeferredRemoval() {
+        if (mRemoveOnExit) {
+            mRemoveOnExit = false;
+            removeImmediately();
+        }
+        return super.handleCompleteDeferredRemoval();
+    }
+
     boolean clearAnimatingFlags() {
         boolean didSomething = false;
         // We don't want to clear it out for windows that get replaced, because the
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 4190a91..94bc22a 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -59,6 +59,9 @@
 
 namespace android {
 
+static bool cancelRunningCompaction;
+static bool compactionInProgress;
+
 // Legacy method for compacting processes, any new code should
 // use compactProcess instead.
 static inline void compactProcessProcfs(int pid, const std::string& compactionType) {
@@ -83,9 +86,18 @@
         // Skip compaction if failed to open pidfd with any error
         return -errno;
     }
+    compactionInProgress = true;
+    cancelRunningCompaction = false;
 
     int64_t totalBytesCompacted = 0;
     for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
+        if (CC_UNLIKELY(cancelRunningCompaction)) {
+            // There could be a significant delay betweenwhen a compaction
+            // is requested and when it is handled during this time
+            // our OOM adjust could have improved.
+            cancelRunningCompaction = false;
+            break;
+        }
         int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
         for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
             vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
@@ -95,11 +107,13 @@
         auto bytesCompacted =
                 process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
         if (CC_UNLIKELY(bytesCompacted == -1)) {
+            compactionInProgress = false;
             return -errno;
         }
 
         totalBytesCompacted += bytesCompacted;
     }
+    compactionInProgress = false;
 
     return totalBytesCompacted;
 }
@@ -228,6 +242,12 @@
     }
 }
 
+static void com_android_server_am_CachedAppOptimizer_cancelCompaction(JNIEnv*, jobject) {
+    if (compactionInProgress) {
+        cancelRunningCompaction = true;
+    }
+}
+
 static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
                                                                     jint compactionFlags) {
     compactProcessOrFallback(pid, compactionFlags);
@@ -279,6 +299,8 @@
 
 static const JNINativeMethod sMethods[] = {
         /* name, signature, funcPtr */
+        {"cancelCompaction", "()V",
+         (void*)com_android_server_am_CachedAppOptimizer_cancelCompaction},
         {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
         {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
         {"freezeBinder", "(IZ)I", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index f2ad068..9fe090a 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -10,6 +10,12 @@
     },
     {
       "name": "CtsIncrementalInstallHostTestCases"
+    },
+    {
+      "name": "libincfs-test"
+    },
+    {
+      "name": "service.incremental_test"
     }
   ],
   "presubmit-large": [
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4673a69..ac90ceb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -310,6 +310,8 @@
             "com.android.clockwork.connectivity.WearConnectivityService";
     private static final String WEAR_POWER_SERVICE_CLASS =
             "com.android.clockwork.power.WearPowerService";
+    private static final String HEALTH_SERVICE_CLASS =
+            "com.google.android.clockwork.healthservices.HealthService";
     private static final String WEAR_SIDEKICK_SERVICE_CLASS =
             "com.google.android.clockwork.sidekick.SidekickService";
     private static final String WEAR_DISPLAYOFFLOAD_SERVICE_CLASS =
@@ -1454,13 +1456,18 @@
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
             t.traceEnd();
 
-            t.traceBegin("StartTelecomLoaderService");
-            mSystemServiceManager.startService(TelecomLoaderService.class);
-            t.traceEnd();
+            // TelecomLoader hooks into classes with defined HFP logic,
+            // so check for either telephony or microphone.
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
+                mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                t.traceBegin("StartTelecomLoaderService");
+                mSystemServiceManager.startService(TelecomLoaderService.class);
+                t.traceEnd();
+            }
 
             t.traceBegin("StartTelephonyRegistry");
             telephonyRegistry = new TelephonyRegistry(
-                    context, new TelephonyRegistry.ConfigurationProvider());
+                context, new TelephonyRegistry.ConfigurationProvider());
             ServiceManager.addService("telephony.registry", telephonyRegistry);
             t.traceEnd();
 
@@ -2494,6 +2501,10 @@
             mSystemServiceManager.startService(WEAR_POWER_SERVICE_CLASS);
             t.traceEnd();
 
+            t.traceBegin("StartHealthService");
+            mSystemServiceManager.startService(HEALTH_SERVICE_CLASS);
+            t.traceEnd();
+
             t.traceBegin("StartWearConnectivityService");
             mSystemServiceManager.startService(WEAR_CONNECTIVITY_SERVICE_CLASS);
             t.traceEnd();
@@ -2566,10 +2577,12 @@
             mActivityManagerService.enterSafeMode();
         }
 
-        // MMS service broker
-        t.traceBegin("StartMmsService");
-        mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
-        t.traceEnd();
+        if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            // MMS service broker
+            t.traceBegin("StartMmsService");
+            mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
+            t.traceEnd();
+        }
 
         if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOFILL)) {
             t.traceBegin("StartAutoFillService");
@@ -2985,9 +2998,7 @@
             t.traceEnd();
             t.traceBegin("MakeTelephonyRegistryReady");
             try {
-                if (telephonyRegistryF != null) {
-                    telephonyRegistryF.systemRunning();
-                }
+                if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
             } catch (Throwable e) {
                 reportWtf("Notifying TelephonyRegistry running", e);
             }
@@ -3001,15 +3012,15 @@
                 reportWtf("Notifying MediaRouterService running", e);
             }
             t.traceEnd();
-            t.traceBegin("MakeMmsServiceReady");
-            try {
-                if (mmsServiceF != null) {
-                    mmsServiceF.systemRunning();
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                t.traceBegin("MakeMmsServiceReady");
+                try {
+                    if (mmsServiceF != null) mmsServiceF.systemRunning();
+                } catch (Throwable e) {
+                    reportWtf("Notifying MmsService running", e);
                 }
-            } catch (Throwable e) {
-                reportWtf("Notifying MmsService running", e);
+                t.traceEnd();
             }
-            t.traceEnd();
 
             t.traceBegin("IncidentDaemonReady");
             try {
@@ -3053,7 +3064,6 @@
     private void startApexServices(@NonNull TimingsTraceAndSlog t) {
         t.traceBegin("startApexServices");
         Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
-        // TODO(satayev): filter out already started services
         // TODO(satayev): introduce android:order for services coming the same apexes
         for (String name : new TreeSet<>(services.keySet())) {
             String jarPath = services.get(name);
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 8538603..635f136 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -40,26 +40,26 @@
     ],
 
     static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.ext.truth",
         "frameworks-base-testutils",
+        "hamcrest-library",
+        "kotlin-test",
+        "mockingservicestests-utils-mockito",
+        "mockito-target-extended-minus-junit4",
+        "platform-test-annotations",
+        "service-blobstore",
+        "service-jobscheduler",
+        "service-permission.impl",
         "services.core",
         "services.devicepolicy",
         "services.net",
         "services.usage",
-        "service-jobscheduler",
-        "service-permission.impl",
-        "service-blobstore",
-        "androidx.test.core",
-        "androidx.test.runner",
-        "androidx.test.ext.truth",
-        "mockito-target-extended-minus-junit4",
-        "platform-test-annotations",
-        "truth-prebuilt",
-        "hamcrest-library",
-        "servicestests-utils-mockito-extended",
-        "mockingservicestests-utils-mockito",
         "servicestests-core-utils",
+        "servicestests-utils-mockito-extended",
         "testables",
-        "kotlin-test",
+        "truth-prebuilt",
         // TODO: remove once Android migrates to JUnit 4.12, which provides assertThrows
         "testng",
     ],
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
new file mode 100644
index 0000000..b65c3e9
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.attention.AttentionManagerInternal;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.BatteryManagerInternal;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerSaveState;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
+import android.test.mock.MockContentResolver;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.lights.LightsManager;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.PowerManagerService.BatteryReceiver;
+import com.android.server.power.PowerManagerService.Injector;
+import com.android.server.power.PowerManagerService.NativeWrapper;
+import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
+import com.android.server.power.batterysaver.BatterySaverController;
+import com.android.server.power.batterysaver.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySaverStateMachine;
+import com.android.server.power.batterysaver.BatterySavingStats;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.power.PowerManagerService}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksServicesTests:PowerManagerServiceMockingTest
+ */
+public class PowerManagerServiceMockingTest {
+    private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+    private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
+    private static final float BRIGHTNESS_FACTOR = 0.7f;
+    private static final boolean BATTERY_SAVER_ENABLED = true;
+
+    @Mock private BatterySaverController mBatterySaverControllerMock;
+    @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
+    @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
+    @Mock private LightsManager mLightsManagerMock;
+    @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
+    @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
+    @Mock private ActivityManagerInternal mActivityManagerInternalMock;
+    @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
+    @Mock private DreamManagerInternal mDreamManagerInternalMock;
+    @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
+    @Mock private Notifier mNotifierMock;
+    @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
+    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+    @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
+    @Mock private DeviceStateManager mDeviceStateManagerMock;
+
+    @Mock
+    private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
+
+    private PowerManagerService mService;
+    private PowerSaveState mPowerSaveState;
+    private ContextWrapper mContextSpy;
+    private BatteryReceiver mBatteryReceiver;
+    private UserSwitchedReceiver mUserSwitchedReceiver;
+    private Resources mResourcesSpy;
+    private OffsettableClock mClock;
+    private TestLooper mTestLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        FakeSettingsProvider.clearSettingsProvider();
+
+        mPowerSaveState = new PowerSaveState.Builder()
+                .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
+                .setBrightnessFactor(BRIGHTNESS_FACTOR)
+                .build();
+        when(mBatterySaverPolicyMock.getBatterySaverPolicy(
+                eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
+                .thenReturn(mPowerSaveState);
+        when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
+        when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
+        when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
+                .thenReturn(true);
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+        when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
+
+        addLocalServiceMock(LightsManager.class, mLightsManagerMock);
+        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
+        addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
+        addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
+        addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
+        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
+
+        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
+        mResourcesSpy = spy(mContextSpy.getResources());
+        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
+
+        MockContentResolver cr = new MockContentResolver(mContextSpy);
+        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContextSpy.getContentResolver()).thenReturn(cr);
+
+        when(mContextSpy.getSystemService(DeviceStateManager.class))
+                .thenReturn(mDeviceStateManagerMock);
+
+        Settings.Global.putInt(mContextSpy.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+
+        mClock = new OffsettableClock.Stopped();
+        mTestLooper = new TestLooper(mClock::now);
+    }
+
+    private PowerManagerService createService() {
+        mService = new PowerManagerService(mContextSpy, new Injector() {
+            @Override
+            Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
+                    SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
+                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) {
+                return mNotifierMock;
+            }
+
+            @Override
+            SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
+                return super.createSuspendBlocker(service, name);
+            }
+
+            @Override
+            BatterySaverPolicy createBatterySaverPolicy(
+                    Object lock, Context context, BatterySavingStats batterySavingStats) {
+                return mBatterySaverPolicyMock;
+            }
+
+            @Override
+            BatterySaverController createBatterySaverController(
+                    Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
+                    BatterySavingStats batterySavingStats) {
+                return mBatterySaverControllerMock;
+            }
+
+            @Override
+            BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
+                    BatterySaverController batterySaverController) {
+                return mBatterySaverStateMachineMock;
+            }
+
+            @Override
+            NativeWrapper createNativeWrapper() {
+                return mNativeWrapperMock;
+            }
+
+            @Override
+            WirelessChargerDetector createWirelessChargerDetector(
+                    SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
+                return mWirelessChargerDetectorMock;
+            }
+
+            @Override
+            AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
+                return mAmbientDisplayConfigurationMock;
+            }
+
+            @Override
+            InattentiveSleepWarningController createInattentiveSleepWarningController() {
+                return mInattentiveSleepWarningControllerMock;
+            }
+
+            @Override
+            public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+                return mSystemPropertiesMock;
+            }
+
+            @Override
+            PowerManagerService.Clock createClock() {
+                return () -> mClock.now();
+            }
+
+            @Override
+            Handler createHandler(Looper looper, Handler.Callback callback) {
+                return new Handler(mTestLooper.getLooper(), callback);
+            }
+
+            @Override
+            void invalidateIsInteractiveCaches() {
+                // Avoids an SELinux failure.
+            }
+        });
+        return mService;
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        LocalServices.removeServiceForTest(LightsManager.class);
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(AttentionManagerInternal.class);
+        LocalServices.removeServiceForTest(DreamManagerInternal.class);
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    /**
+     * Creates a mock and registers it to {@link LocalServices}.
+     */
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
+    private void advanceTime(long timeMs) {
+        mClock.fastForward(timeMs);
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void testUserActivityOnDeviceStateChange() {
+        createService();
+        mService.systemReady(null);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        final DisplayInfo info = new DisplayInfo();
+        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+        final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
+                ArgumentCaptor.forClass(DeviceStateCallback.class);
+        verify(mDeviceStateManagerMock).registerCallback(any(),
+                deviceStateCallbackCaptor.capture());
+
+        // Advance the time 10001 and verify that the device thinks it has been idle
+        // for just less than that.
+        mService.onUserActivity();
+        advanceTime(10001);
+        assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
+
+        // Send a display state change event and advance the clock 10.
+        final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
+        deviceStateCallback.onStateChanged(1);
+        final long timeToAdvance = 10;
+        advanceTime(timeToAdvance);
+
+        // Ensure that the device has been idle for only 10 (doesn't include the idle time
+        // before the display state event).
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
+
+        // Send the same state and ensure that does not trigger an update.
+        deviceStateCallback.onStateChanged(1);
+        advanceTime(timeToAdvance);
+        final long newTime = timeToAdvance * 2;
+
+        assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java
index 4413dc8..f92f5ea 100644
--- a/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/SystemServiceManagerTest.java
@@ -23,6 +23,7 @@
 import org.junit.Test;
 
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 
 /**
@@ -32,24 +33,52 @@
 
     private static final String TAG = "SystemServiceManagerTest";
 
+    private final SystemServiceManager mSystemServiceManager =
+            new SystemServiceManager(getContext());
+
     @Test
     public void testSealStartedServices() throws Exception {
-        SystemServiceManager manager = new SystemServiceManager(getContext());
         // must be effectively final, since it's changed from inner class below
         AtomicBoolean serviceStarted = new AtomicBoolean(false);
-        SystemService service = new SystemService(getContext()) {
+        SystemService service1 = new SystemService(getContext()) {
             @Override
             public void onStart() {
                 serviceStarted.set(true);
             }
         };
+        SystemService service2 = new SystemService(getContext()) {
+            @Override
+            public void onStart() {
+                throw new IllegalStateException("Second service must not be called");
+            }
+        };
 
         // started services have their #onStart methods called
-        manager.startService(service);
+        mSystemServiceManager.startService(service1);
         assertTrue(serviceStarted.get());
 
         // however, after locking started services, it is not possible to start a new service
-        manager.sealStartedServices();
-        assertThrows(UnsupportedOperationException.class, () -> manager.startService(service));
+        mSystemServiceManager.sealStartedServices();
+        assertThrows(UnsupportedOperationException.class,
+                () -> mSystemServiceManager.startService(service2));
     }
+
+    @Test
+    public void testDuplicateServices() throws Exception {
+        AtomicInteger counter = new AtomicInteger(0);
+        SystemService service = new SystemService(getContext()) {
+            @Override
+            public void onStart() {
+                counter.incrementAndGet();
+            }
+        };
+
+        mSystemServiceManager.startService(service);
+        assertEquals(1, counter.get());
+
+        // manager does not start the same service twice
+        mSystemServiceManager.startService(service);
+        assertEquals(1, counter.get());
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index cffff66..02cf971 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -23,7 +23,14 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -105,6 +112,7 @@
     public void tearDown() throws Exception {
         mKeyStore.deleteKeyStore();
         setAllowedConnectionTime(mOriginalAllowedConnectionTime);
+        dropShellPermissionIdentity();
     }
 
     /**
@@ -813,6 +821,108 @@
         return hasAtLeastOneLetter;
     }
 
+    CountDownLatch mAdbActionLatch = new CountDownLatch(1);
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            Log.i(TAG, "Received intent action=" + action);
+            if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra(
+                        AdbManager.WIRELESS_DEVICES_EXTRA).toString());
+                mAdbActionLatch.countDown();
+            } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_DISCONNECTED);
+                Log.i(TAG, "action=" + action + " status=" + status);
+                mAdbActionLatch.countDown();
+            } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+                assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+                        context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                        PackageManager.PERMISSION_GRANTED);
+                Integer res = intent.getIntExtra(
+                        AdbManager.WIRELESS_STATUS_EXTRA,
+                        AdbManager.WIRELESS_STATUS_FAIL);
+                Log.i(TAG, "action=" + action + " result=" + res);
+
+                if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
+                    String pairingCode = intent.getStringExtra(
+                                AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
+                    Log.i(TAG, "pairingCode=" + pairingCode);
+                } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+                    int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+                    Log.i(TAG, "port=" + port);
+                }
+                mAdbActionLatch.countDown();
+            }
+        }
+    };
+
+    private void adoptShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+            .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING);
+    }
+
+    private void dropShellPermissionIdentity() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+            .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void testBroadcastReceiverWithPermissions() throws Exception {
+        adoptShellPermissionIdentity();
+        final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+                ServiceManager.getService(Context.ADB_SERVICE));
+        IntentFilter intentFilter =
+                new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+        assertEquals("Context does not have MANAGE_DEBUGGING permission.",
+                mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                PackageManager.PERMISSION_GRANTED);
+        try {
+            mContext.registerReceiver(mReceiver, intentFilter);
+            mAdbManager.enablePairingByPairingCode();
+            if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+                fail("Receiver did not receive adb intent action within the timeout duration");
+            }
+        } finally {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
+    @Test
+    public void testBroadcastReceiverWithoutPermissions() throws Exception {
+        adoptShellPermissionIdentity();
+        final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+                ServiceManager.getService(Context.ADB_SERVICE));
+        IntentFilter intentFilter =
+                new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+        intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+        mAdbManager.enablePairingByPairingCode();
+
+        dropShellPermissionIdentity();
+        assertEquals("Context has MANAGE_DEBUGGING permission.",
+                mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+                PackageManager.PERMISSION_DENIED);
+        try {
+            mContext.registerReceiver(mReceiver, intentFilter);
+
+            if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+                fail("Broadcast receiver received adb action intent without debug permissions");
+            }
+        } finally {
+            mContext.unregisterReceiver(mReceiver);
+        }
+    }
+
     /**
      * Runs an adb test with the provided configuration.
      *
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java
new file mode 100644
index 0000000..a53e22e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/LockoutResetDispatcherTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IRemoteCallback;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@Presubmit
+@SmallTest
+public class LockoutResetDispatcherTest {
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
+
+    @Mock
+    private IBinder mBinder;
+    @Mock
+    private IBiometricServiceLockoutResetCallback mCallback;
+
+    private LockoutResetDispatcher mDispatcher;
+
+    @Before
+    public void setup() {
+        when(mCallback.asBinder()).thenReturn(mBinder);
+        mDispatcher = new LockoutResetDispatcher(mContext);
+    }
+
+    @Test
+    public void linksToDeath() throws Exception {
+        mDispatcher.addCallback(mCallback, "package");
+        verify(mBinder).linkToDeath(eq(mDispatcher), anyInt());
+    }
+
+    @Test
+    public void notifyLockoutReset() throws Exception {
+        final int sensorId = 24;
+
+        mDispatcher.addCallback(mCallback, "some.package");
+        mDispatcher.notifyLockoutResetCallbacks(sensorId);
+
+        final ArgumentCaptor<IRemoteCallback> captor =
+                ArgumentCaptor.forClass(IRemoteCallback.class);
+        verify(mCallback).onLockoutReset(eq(sensorId), captor.capture());
+        captor.getValue().sendResult(new Bundle());
+    }
+
+    @Test
+    public void releaseWakeLockOnDeath() {
+        mDispatcher.addCallback(mCallback, "a.b.cee");
+        mDispatcher.binderDied(mBinder);
+
+        // would be better to check the wake lock
+        // but this project lacks the extended mockito support to do it
+        assertThat(mDispatcher.mClientCallbacks).isEmpty();
+    }
+
+    @Test
+    public void releaseCorrectWakeLockOnDeath() {
+        mDispatcher.addCallback(mCallback, "a.b");
+        mDispatcher.binderDied(mock(IBinder.class));
+
+        assertThat(mDispatcher.mClientCallbacks).hasSize(1);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 26b34fd..304fe5a 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -30,6 +30,7 @@
 import android.hardware.power.stats.State;
 import android.hardware.power.stats.StateResidency;
 import android.hardware.power.stats.StateResidencyResult;
+import android.os.Looper;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -145,12 +146,12 @@
         }
 
         @Override
-        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
-                String meterFilename, String meterCacheFilename,
+        PowerStatsLogger createPowerStatsLogger(Context context, Looper looper,
+                File dataStoragePath, String meterFilename, String meterCacheFilename,
                 String modelFilename, String modelCacheFilename,
                 String residencyFilename, String residencyCacheFilename,
                 IPowerStatsHALWrapper powerStatsHALWrapper) {
-            mPowerStatsLogger = new PowerStatsLogger(context, dataStoragePath,
+            mPowerStatsLogger = new PowerStatsLogger(context, looper, dataStoragePath,
                 meterFilename, meterCacheFilename,
                 modelFilename, modelCacheFilename,
                 residencyFilename, residencyCacheFilename,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
index e2a348e..4556a4a 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibrator.java
@@ -17,6 +17,7 @@
 package com.android.server.vibrator;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -24,37 +25,8 @@
 /** Fake implementation of {@link Vibrator} for service tests. */
 final class FakeVibrator extends Vibrator {
 
-    private int mDefaultHapticFeedbackIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-    private int mDefaultNotificationIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-    private int mDefaultRingIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
-
-    @Override
-    public int getDefaultHapticFeedbackIntensity() {
-        return mDefaultHapticFeedbackIntensity;
-    }
-
-    @Override
-    public int getDefaultNotificationVibrationIntensity() {
-        return mDefaultNotificationIntensity;
-    }
-
-    @Override
-    public int getDefaultRingVibrationIntensity() {
-        return mDefaultRingIntensity;
-    }
-
-    public void setDefaultHapticFeedbackIntensity(
-            @VibrationIntensity int defaultHapticFeedbackIntensity) {
-        mDefaultHapticFeedbackIntensity = defaultHapticFeedbackIntensity;
-    }
-
-    public void setDefaultNotificationVibrationIntensity(
-            @VibrationIntensity int defaultNotificationIntensity) {
-        mDefaultNotificationIntensity = defaultNotificationIntensity;
-    }
-
-    public void setDefaultRingVibrationIntensity(@VibrationIntensity int defaultRingIntensity) {
-        mDefaultRingIntensity = defaultRingIntensity;
+    FakeVibrator(Context context) {
+        super(context);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
index 59c0b0e..6369dbc 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java
@@ -16,6 +16,14 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
+import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationAttributes.USAGE_TOUCH;
+import static android.os.Vibrator.VIBRATION_INTENSITY_HIGH;
+import static android.os.Vibrator.VIBRATION_INTENSITY_LOW;
+import static android.os.Vibrator.VIBRATION_INTENSITY_MEDIUM;
+import static android.os.Vibrator.VIBRATION_INTENSITY_OFF;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertTrue;
 
@@ -24,7 +32,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.ContextWrapper;
 import android.os.Handler;
 import android.os.IExternalVibratorService;
@@ -37,6 +44,7 @@
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
@@ -68,29 +76,31 @@
     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Mock private PowerManagerInternal mPowerManagerInternalMock;
+    @Mock private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
-    private FakeVibrator mFakeVibrator;
     private VibrationSettings mVibrationSettings;
     private VibrationScaler mVibrationScaler;
 
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mFakeVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
 
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
 
+        Settings.System.putInt(contentResolver, Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
+        Settings.System.putInt(contentResolver, Settings.System.VIBRATE_WHEN_RINGING, 1);
+
         mVibrationSettings = new VibrationSettings(
-                mContextSpy, new Handler(mTestLooper.getLooper()));
+                mContextSpy, new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
         mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
+
         mVibrationSettings.onSystemReady();
     }
 
@@ -101,91 +111,80 @@
 
     @Test
     public void testGetExternalVibrationScale() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
         assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
         assertEquals(IExternalVibratorService.SCALE_HIGH,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
         assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
         assertEquals(IExternalVibratorService.SCALE_LOW,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH);
         assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
 
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         // Unexpected vibration intensity will be treated as SCALE_NONE.
         assertEquals(IExternalVibratorService.SCALE_NONE,
-                mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
+                mVibrationScaler.getExternalVibrationScale(USAGE_TOUCH));
     }
 
     @Test
     public void scale_withPrebakedSegment_setsEffectStrengthBasedOnSettings() {
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         PrebakedSegment effect = new PrebakedSegment(VibrationEffect.EFFECT_CLICK,
                 /* shouldFallback= */ false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        PrebakedSegment scaled = mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION);
+        PrebakedSegment scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
+                VIBRATION_INTENSITY_MEDIUM);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
-        scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        scaled = mVibrationScaler.scale(effect, USAGE_NOTIFICATION);
         // Unexpected intensity setting will be mapped to STRONG.
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
     }
 
     @Test
     public void scale_withPrebakedEffect_setsEffectStrengthBasedOnSettings() {
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
 
         PrebakedSegment scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+                effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
 
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+                VIBRATION_INTENSITY_MEDIUM);
         scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+                effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
         scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+                effect, USAGE_NOTIFICATION));
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
 
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         scaled = getFirstSegment(mVibrationScaler.scale(
-                effect, VibrationAttributes.USAGE_NOTIFICATION));
+                effect, USAGE_NOTIFICATION));
         // Unexpected intensity setting will be mapped to STRONG.
         assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
     }
@@ -193,81 +192,77 @@
     @Test
     public void scale_withOneShotAndWaveform_resolvesAmplitude() {
         // No scale, default amplitude still resolved
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
 
         StepSegment resolved = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE),
-                VibrationAttributes.USAGE_RINGTONE));
+                USAGE_RINGTONE));
         assertTrue(resolved.getAmplitude() > 0);
 
         resolved = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createWaveform(new long[]{10},
                         new int[]{VibrationEffect.DEFAULT_AMPLITUDE}, -1),
-                VibrationAttributes.USAGE_RINGTONE));
+                USAGE_RINGTONE));
         assertTrue(resolved.getAmplitude() > 0);
     }
 
     @Test
     public void scale_withOneShotAndWaveform_scalesAmplitude() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
 
         StepSegment scaled = getFirstSegment(mVibrationScaler.scale(
-                VibrationEffect.createOneShot(128, 128), VibrationAttributes.USAGE_RINGTONE));
+                VibrationEffect.createOneShot(128, 128), USAGE_RINGTONE));
         // Ringtone scales up.
         assertTrue(scaled.getAmplitude() > 0.5);
 
         scaled = getFirstSegment(mVibrationScaler.scale(
                 VibrationEffect.createWaveform(new long[]{128}, new int[]{128}, -1),
-                VibrationAttributes.USAGE_NOTIFICATION));
+                USAGE_NOTIFICATION));
         // Notification scales down.
         assertTrue(scaled.getAmplitude() < 0.5);
 
         scaled = getFirstSegment(mVibrationScaler.scale(VibrationEffect.createOneShot(128, 128),
-                VibrationAttributes.USAGE_TOUCH));
+                USAGE_TOUCH));
         // Haptic feedback does not scale.
         assertEquals(128f / 255, scaled.getAmplitude(), 1e-5);
     }
 
     @Test
     public void scale_withComposed_scalesPrimitives() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_HIGH);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(USAGE_NOTIFICATION, VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM);
 
         VibrationEffect composed = VibrationEffect.startComposition()
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f).compose();
 
-        PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed,
-                VibrationAttributes.USAGE_RINGTONE));
+        PrimitiveSegment scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_RINGTONE));
         // Ringtone scales up.
         assertTrue(scaled.getScale() > 0.5f);
 
-        scaled = getFirstSegment(mVibrationScaler.scale(composed,
-                VibrationAttributes.USAGE_NOTIFICATION));
+        scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_NOTIFICATION));
         // Notification scales down.
         assertTrue(scaled.getScale() < 0.5f);
 
-        scaled = getFirstSegment(mVibrationScaler.scale(composed, VibrationAttributes.USAGE_TOUCH));
+        scaled = getFirstSegment(mVibrationScaler.scale(composed, USAGE_TOUCH));
         // Haptic feedback does not scale.
         assertEquals(0.5, scaled.getScale(), 1e-5);
     }
 
+    private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
+            @Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
+    }
+
     private <T extends VibrationEffectSegment> T getFirstSegment(VibrationEffect.Composed effect) {
         return (T) effect.getSegments().get(0);
     }
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index ab9fbb5..ff59d0f 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -16,9 +16,11 @@
 
 package com.android.server.vibrator;
 
+import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
 import static android.os.VibrationAttributes.USAGE_ALARM;
 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
+import static android.os.VibrationAttributes.USAGE_MEDIA;
 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
 import static android.os.VibrationAttributes.USAGE_RINGTONE;
@@ -35,6 +37,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
@@ -45,7 +48,6 @@
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
-import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.media.AudioManager;
@@ -55,7 +57,9 @@
 import android.os.UserHandle;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.os.test.TestLooper;
+import android.os.vibrator.VibrationConfig;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 
@@ -87,6 +91,19 @@
     private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
             .setBatterySaverEnabled(true).build();
 
+    private static final int[] ALL_USAGES = new int[] {
+            USAGE_UNKNOWN,
+            USAGE_ACCESSIBILITY,
+            USAGE_ALARM,
+            USAGE_COMMUNICATION_REQUEST,
+            USAGE_HARDWARE_FEEDBACK,
+            USAGE_MEDIA,
+            USAGE_NOTIFICATION,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_RINGTONE,
+            USAGE_TOUCH,
+    };
+
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
     @Rule
@@ -96,23 +113,23 @@
     private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
     @Mock
     private PowerManagerInternal mPowerManagerInternalMock;
+    @Mock
+    private VibrationConfig mVibrationConfigMock;
 
     private TestLooper mTestLooper;
     private ContextWrapper mContextSpy;
     private AudioManager mAudioManager;
-    private FakeVibrator mFakeVibrator;
     private VibrationSettings mVibrationSettings;
     private PowerManagerInternal.LowPowerModeListener mRegisteredPowerModeListener;
 
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mFakeVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
-        when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mFakeVibrator);
+
         doAnswer(invocation -> {
             mRegisteredPowerModeListener = invocation.getArgument(0);
             return null;
@@ -121,16 +138,18 @@
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
 
+        setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
         mAudioManager = mContextSpy.getSystemService(AudioManager.class);
         mVibrationSettings = new VibrationSettings(mContextSpy,
-                new Handler(mTestLooper.getLooper()));
-        mVibrationSettings.onSystemReady();
+                new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
 
         // Simulate System defaults.
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
         setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+        mVibrationSettings.onSystemReady();
     }
 
     @After
@@ -145,12 +164,15 @@
         setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        verify(mListenerMock, times(7)).onChange();
+        verify(mListenerMock, times(10)).onChange();
     }
 
     @Test
@@ -192,9 +214,7 @@
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
 
         for (int usage : expectedAllowedVibrations) {
-            assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationNotIgnoredForUsage(usage);
         }
     }
 
@@ -209,10 +229,7 @@
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
 
         for (int usage : expectedIgnoredVibrations) {
-            assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
-                    Vibration.Status.IGNORED_BACKGROUND,
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_BACKGROUND);
         }
     }
 
@@ -221,10 +238,9 @@
         mVibrationSettings.mUidObserver.onUidStateChanged(
                 UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
@@ -238,9 +254,7 @@
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : expectedAllowedVibrations) {
-            assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationNotIgnoredForUsage(usage);
         }
     }
 
@@ -257,10 +271,7 @@
         mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
 
         for (int usage : expectedIgnoredVibrations) {
-            assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
-                    Vibration.Status.IGNORED_FOR_POWER,
-                    mVibrationSettings.shouldIgnoreVibration(UID,
-                            VibrationAttributes.createForUsage(usage)));
+            assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_POWER);
         }
     }
 
@@ -268,130 +279,130 @@
     public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
         mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
         // Vibrating settings on are overruled by ringer mode.
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         setRingerMode(AudioManager.RINGER_MODE_SILENT);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE || usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
-        // Vibrating settings off are overruled by ringer mode.
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOff_ignoresRingtoneOnly() {
-        // Vibrating settings off are respected for normal ringer mode.
-        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
-        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+    public void shouldIgnoreVibration_withRingerModeNormal_allowsAllVibrations() {
         setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOn_allowsAllVibrations() {
+    public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() {
+        setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+        setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
+    }
+
+    @Test
+    public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() {
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
     }
 
     @Test
-    public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() {
+    public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() {
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
-        setRingerMode(AudioManager.RINGER_MODE_NORMAL);
 
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+        for (int usage : ALL_USAGES) {
+            assertVibrationNotIgnoredForUsage(usage);
+        }
+    }
+
+    @Test
+    public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() {
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0);
+
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() {
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_TOUCH) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() {
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
     public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() {
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_NOTIFICATION) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
@@ -402,15 +413,13 @@
         setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
-                mVibrationSettings.shouldIgnoreVibration(UID,
-                        VibrationAttributes.createForUsage(USAGE_RINGTONE)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_ALARM)));
-        assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
-                VibrationAttributes.createForUsage(USAGE_TOUCH)));
+        for (int usage : ALL_USAGES) {
+            if (usage == USAGE_RINGTONE) {
+                assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS);
+            } else {
+                assertVibrationNotIgnoredForUsage(usage);
+            }
+        }
     }
 
     @Test
@@ -423,90 +432,40 @@
     }
 
     @Test
-    public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_HIGH);
-
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+    public void getDefaultIntensity_returnsIntensityFromVibratorConfig() {
+        setDefaultIntensity(VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
-
-        VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
-                new Handler(mTestLooper.getLooper()));
-
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                vibrationSettings.getDefaultIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                vibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
-    }
-
-    @Test
-    public void getDefaultIntensity_returnsIntensityFromVibratorService() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_MEDIUM);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_LOW);
-
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
 
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_HIGH,
-                mVibrationSettings.getDefaultIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getDefaultIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getDefaultIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getDefaultIntensity(USAGE_RINGTONE));
+        for (int usage : ALL_USAGES) {
+            assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getDefaultIntensity(usage));
+        }
     }
 
     @Test
     public void getCurrentIntensity_returnsIntensityFromSettings() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_OFF);
-        mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_OFF);
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
-
-        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        setDefaultIntensity(VIBRATION_INTENSITY_OFF);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
         setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW);
 
-        assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_ALARM));
-        assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getCurrentIntensity(USAGE_NOTIFICATION));
-        assertEquals(VIBRATION_INTENSITY_MEDIUM,
-                mVibrationSettings.getCurrentIntensity(USAGE_UNKNOWN));
-        assertEquals(VIBRATION_INTENSITY_LOW,
-                mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+        for (int usage : ALL_USAGES) {
+            assertEquals(errorMessageForUsage(usage),
+                    VIBRATION_INTENSITY_LOW,
+                    mVibrationSettings.getCurrentIntensity(usage));
+        }
     }
 
     @Test
     public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
-        mFakeVibrator.setDefaultRingVibrationIntensity(VIBRATION_INTENSITY_OFF);
+        setDefaultIntensity(USAGE_RINGTONE, VIBRATION_INTENSITY_OFF);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_HIGH);
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
@@ -524,8 +483,9 @@
 
     @Test
     public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() {
-        mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_MEDIUM);
+        setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+        mVibrationSettings.updateSettings();
         assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
         // If haptic feedback is off, fallback to default value.
         assertEquals(VIBRATION_INTENSITY_MEDIUM,
@@ -533,15 +493,11 @@
         assertEquals(VIBRATION_INTENSITY_MEDIUM,
                 mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION));
 
-        // Switching user is not working with FakeSettingsProvider.
-        // Testing the broadcast flow manually.
-        Settings.System.putIntForUser(mContextSpy.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH,
-                UserHandle.USER_CURRENT);
-        mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
-                new Intent(Intent.ACTION_USER_SWITCHED));
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH);
+        mVibrationSettings.updateSettings();
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_TOUCH));
+        // If haptic feedback is on, fallback to that value.
         assertEquals(VIBRATION_INTENSITY_HIGH,
                 mVibrationSettings.getCurrentIntensity(USAGE_HARDWARE_FEEDBACK));
         assertEquals(VIBRATION_INTENSITY_HIGH,
@@ -557,6 +513,33 @@
         assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_DOUBLE_CLICK));
     }
 
+    private void assertVibrationIgnoredForUsage(@VibrationAttributes.Usage int usage,
+            Vibration.Status expectedStatus) {
+        assertEquals(errorMessageForUsage(usage),
+                expectedStatus,
+                mVibrationSettings.shouldIgnoreVibration(UID,
+                        VibrationAttributes.createForUsage(usage)));
+    }
+
+    private void assertVibrationNotIgnoredForUsage(@VibrationAttributes.Usage int usage) {
+        assertNull(errorMessageForUsage(usage),
+                mVibrationSettings.shouldIgnoreVibration(UID,
+                        VibrationAttributes.createForUsage(usage)));
+    }
+
+    private String errorMessageForUsage(int usage) {
+        return "Error for usage " + VibrationAttributes.usageToString(usage);
+    }
+
+    private void setDefaultIntensity(@Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt())).thenReturn(intensity);
+    }
+
+    private void setDefaultIntensity(@VibrationAttributes.Usage int usage,
+            @Vibrator.VibrationIntensity int intensity) {
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(eq(usage))).thenReturn(intensity);
+    }
+
     private void setUserSetting(String settingName, int value) {
         Settings.System.putIntForUser(
                 mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index bfceb9a..5dd44ff 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -44,11 +44,13 @@
 import android.os.SystemClock;
 import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.os.test.TestLooper;
 import android.os.vibrator.PrebakedSegment;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.RampSegment;
 import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationConfig;
 import android.os.vibrator.VibrationEffectSegment;
 import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.Presubmit;
@@ -101,6 +103,8 @@
     private IBinder mVibrationToken;
     @Mock
     private IBatteryStats mIBatteryStatsMock;
+    @Mock
+    private VibrationConfig mVibrationConfigMock;
 
     private final Map<Integer, FakeVibratorControllerProvider> mVibratorProviders = new HashMap<>();
     private VibrationSettings mVibrationSettings;
@@ -113,9 +117,13 @@
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
 
+        when(mVibrationConfigMock.getDefaultVibrationIntensity(anyInt()))
+                .thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        when(mVibrationConfigMock.getRampStepDurationMs()).thenReturn(TEST_RAMP_STEP_DURATION);
+
         Context context = InstrumentationRegistry.getContext();
         mVibrationSettings = new VibrationSettings(context, new Handler(mTestLooper.getLooper()),
-                /* rampDownDuration= */ 0, TEST_RAMP_STEP_DURATION);
+                mVibrationConfigMock);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mWakeLock = context.getSystemService(
                 PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
@@ -1111,9 +1119,7 @@
 
     @Test
     public void vibrate_waveformWithRampDown_addsRampDownAfterVibrationCompleted() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1139,9 +1145,7 @@
 
     @Test
     public void vibrate_waveformWithRampDown_triggersCallbackWhenOriginalVibrationEnds() {
-        int rampDownDuration = 10_000;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(10_000);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1174,9 +1178,7 @@
     @Test
     public void vibrate_waveformCancelledWithRampDown_addsRampDownAfterVibrationCancelled()
             throws Exception {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
 
@@ -1202,9 +1204,7 @@
 
     @Test
     public void vibrate_predefinedWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
         mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
@@ -1224,9 +1224,7 @@
 
     @Test
     public void vibrate_composedWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
                 IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1251,9 +1249,7 @@
 
     @Test
     public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
-        int rampDownDuration = 15;
-        mVibrationSettings = new VibrationSettings(InstrumentationRegistry.getContext(),
-                new Handler(mTestLooper.getLooper()), rampDownDuration, TEST_RAMP_STEP_DURATION);
+        when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
         mEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
         fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index c0f7596..b0bdaf0 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -155,12 +156,13 @@
     @Before
     public void setUp() throws Exception {
         mTestLooper = new TestLooper();
-        mVibrator = new FakeVibrator();
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         InputManager inputManager = InputManager.resetInstance(mIInputManagerMock);
 
         ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
         when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
+
+        mVibrator = new FakeVibrator(mContextSpy);
         when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibrator);
         when(mContextSpy.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager);
         when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
@@ -175,8 +177,13 @@
         }).when(mPowerManagerInternalMock).registerLowPowerModeObserver(any());
 
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+        setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
                 Vibrator.VIBRATION_INTENSITY_MEDIUM);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
@@ -437,7 +444,7 @@
                 UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
 
         PrebakedSegment expected = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         // Only vibrators 1 and 3 have always-on capabilities.
         assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expected);
@@ -461,10 +468,10 @@
                 UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
 
         PrebakedSegment expectedClick = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         PrebakedSegment expectedTick = new PrebakedSegment(
-                VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
+                VibrationEffect.EFFECT_TICK, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
 
         // Enables click on vibrator 1 and tick on vibrator 2 only.
         assertEquals(mVibratorProviders.get(1).getAlwaysOnEffect(1), expectedClick);
@@ -539,7 +546,6 @@
     public void vibrate_withRingtone_usesRingtoneSettings() throws Exception {
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
-        mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
         fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK,
                 VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK);
 
@@ -932,55 +938,67 @@
 
     @Test
     public void vibrate_withIntensitySettings_appliesSettingsToScaleVibrations() throws Exception {
-        mVibrator.setDefaultNotificationVibrationIntensity(Vibrator.VIBRATION_INTENSITY_LOW);
+        int defaultNotificationIntensity =
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION);
         setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_HIGH);
+                defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH
+                        ? defaultNotificationIntensity + 1
+                        : defaultNotificationIntensity);
+
+        int defaultTouchIntensity =
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH);
         setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_LOW);
-        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
-                Vibrator.VIBRATION_INTENSITY_OFF);
+                defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW
+                        ? defaultTouchIntensity - 1
+                        : defaultTouchIntensity);
+
+        setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
+                mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
+        setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY,
+                Vibrator.VIBRATION_INTENSITY_HIGH);
+        setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
 
         mockVibrators(1);
         FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
         fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
                 IVibrator.CAP_COMPOSE_EFFECTS);
-        fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
         VibratorManagerService service = createSystemReadyService();
 
         vibrate(service, CombinedVibration.startSequential()
-                .addNext(1, VibrationEffect.createOneShot(20, 100))
+                .addNext(1, VibrationEffect.createOneShot(100, 125))
                 .combine(), NOTIFICATION_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 1,
                 service, TEST_TIMEOUT_MILLIS));
 
         vibrate(service, VibrationEffect.startComposition()
-                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
                 .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
                 .compose(), HAPTIC_FEEDBACK_ATTRS);
+        assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 2,
+                service, TEST_TIMEOUT_MILLIS));
+
+        vibrate(service, VibrationEffect.startComposition()
+                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+                .compose(), ALARM_ATTRS);
         assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 3,
                 service, TEST_TIMEOUT_MILLIS));
 
-        vibrate(service, CombinedVibration.startParallel()
-                .addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
-                .combine(), ALARM_ATTRS);
-        assertTrue(waitUntil(s -> fakeVibrator.getEffectSegments().size() == 4,
+        vibrate(service, VibrationEffect.createOneShot(100, 125), RINGTONE_ATTRS);
+        assertFalse(waitUntil(s -> fakeVibrator.getEffectSegments().size() > 3,
                 service, TEST_TIMEOUT_MILLIS));
 
-        vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS);
+        assertEquals(3, fakeVibrator.getEffectSegments().size());
 
-        assertEquals(4, fakeVibrator.getEffectSegments().size());
+        // Notification vibrations will be scaled with SCALE_HIGH or none if default is high.
+        assertEquals(defaultNotificationIntensity < Vibrator.VIBRATION_INTENSITY_HIGH,
+                0.6 < fakeVibrator.getAmplitudes().get(0));
 
-        // Notification vibrations will be scaled with SCALE_VERY_HIGH.
-        assertTrue(0.6 < fakeVibrator.getAmplitudes().get(0));
+        // Haptic feedback vibrations will be scaled with SCALE_LOW or none if default is low.
+        assertEquals(defaultTouchIntensity > Vibrator.VIBRATION_INTENSITY_LOW,
+                0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale());
 
-        // Haptic feedback vibrations will be scaled with SCALE_LOW.
-        assertTrue(0.5 < ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(1)).getScale());
-        assertTrue(0.5 > ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale());
-
-        // Alarm vibration is always VIBRATION_INTENSITY_HIGH.
-        PrebakedSegment expected = new PrebakedSegment(
-                VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_STRONG);
-        assertEquals(expected, fakeVibrator.getEffectSegments().get(3));
+        // Alarm vibration will be scaled with SCALE_NONE.
+        assertEquals(1f,
+                ((PrimitiveSegment) fakeVibrator.getEffectSegments().get(2)).getScale(), 1e-5);
 
         // Ring vibrations have intensity OFF and are not played.
     }
@@ -1100,7 +1118,7 @@
         int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
         mExternalVibratorService.onExternalVibrationStop(externalVibration);
 
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
         assertEquals(Arrays.asList(false, true, false),
                 mVibratorProviders.get(1).getExternalControlStates());
     }
@@ -1127,8 +1145,8 @@
                 ringtoneAudioAttrs, secondController);
         int secondScale = mExternalVibratorService.onExternalVibrationStart(secondVibration);
 
-        assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
-        assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, firstScale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, secondScale);
         verify(firstController).mute();
         verify(secondController, never()).mute();
         // Set external control called only once.
@@ -1151,7 +1169,7 @@
         ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, AUDIO_ATTRS,
                 mock(IExternalVibrationController.class));
         int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
         // Vibration is cancelled.
         assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
@@ -1163,7 +1181,6 @@
     public void onExternalVibration_withRingtone_usesRingerModeSettings() {
         mockVibrators(1);
         mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
-        mVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_MEDIUM);
         AudioAttributes audioAttrs = new AudioAttributes.Builder()
                 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
                 .build();
@@ -1181,13 +1198,13 @@
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
 
         setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
         setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
         createSystemReadyService();
         scale = mExternalVibratorService.onExternalVibrationStart(externalVibration);
-        assertEquals(IExternalVibratorService.SCALE_NONE, scale);
+        assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale);
     }
 
     private VibrationEffectSegment expectedPrebaked(int effectId) {
@@ -1235,10 +1252,6 @@
                 mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
     }
 
-    private void setGlobalSetting(String settingName, int value) {
-        Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
-    }
-
     private void vibrate(VibratorManagerService service, VibrationEffect effect,
             VibrationAttributes attrs) {
         vibrate(service, CombinedVibration.createParallel(effect), attrs);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 11777ef..d831903 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -182,7 +182,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -7174,6 +7173,14 @@
     }
 
     @Test
+    public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception {
+        assertEquals(mInternalService.areNotificationsEnabledForPackage(
+                mContext.getPackageName(), mUid),
+                mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid));
+        verify(mPermissionHelper, never()).hasPermission(anyInt());
+    }
+
+    @Test
     public void testAreBubblesAllowedForPackage_crossUser() throws Exception {
         try {
             mBinderService.getBubblePreferenceForPackage(mContext.getPackageName(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 0c8fe35..1362628 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -536,6 +536,12 @@
     }
 
     @Test
+    public void testAreNotificationsEnabledForPackage_viaInternalService() {
+        mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
+        verify(mPermissionHelper).hasPermission(mUid);
+    }
+
+    @Test
     public void testGetPackageImportance() throws Exception {
         when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
         assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 525888d..cb9eb52 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -30,7 +30,6 @@
 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
-import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
@@ -197,25 +196,6 @@
     }
 
     @Test
-    public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() {
-        final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
-                resourcesWithProvider(""));
-        final DisplayAreaPolicyBuilder.Result defaultPolicy =
-                (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
-                        mRoot, mImeContainer);
-        if (mDisplayContent.isDefaultDisplay) {
-            final List<Feature> features = defaultPolicy.getFeatures();
-            boolean hasOneHandedBackgroundFeature = false;
-            for (Feature feature : features) {
-                hasOneHandedBackgroundFeature |=
-                        feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL;
-            }
-
-            assertThat(hasOneHandedBackgroundFeature).isTrue();
-        }
-    }
-
-    @Test
     public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
                 resourcesWithProvider(""));
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index d94fafc..ce9530c 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -701,8 +701,15 @@
          */
         public static final int PROPERTY_CROSS_SIM = 0x00004000;
 
+        /**
+         * Connection is a tethered external call.
+         * Indicates that the {@link Connection} is fixed on this device but the audio streams are
+         * re-routed to another device.
+         */
+        public static final int PROPERTY_TETHERED_CALL = 0x00008000;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00004000
+        // Next PROPERTY value: 0x00010000
         //******************************************************************************************
 
         private final @CallState int mState;
@@ -899,6 +906,9 @@
             if (hasProperty(properties, PROPERTY_CROSS_SIM)) {
                 builder.append(" PROPERTY_CROSS_SIM");
             }
+            if (hasProperty(properties, PROPERTY_TETHERED_CALL)) {
+                builder.append(" PROPERTY_TETHERED_CALL");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index fccdf76..389df80 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -27,7 +27,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -42,7 +41,8 @@
 public final class CallAudioState implements Parcelable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+    @IntDef(value = {ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER,
+            ROUTE_EXTERNAL},
             flag=true)
     public @interface CallAudioRoute {}
 
@@ -58,6 +58,9 @@
     /** Direct the audio stream through the device's speakerphone. */
     public static final int ROUTE_SPEAKER       = 0x00000008;
 
+    /** Direct the audio stream through another device. */
+    public static final int ROUTE_EXTERNAL       = 0x00000010;
+
     /**
      * Direct the audio stream through the device's earpiece or wired headset if one is
      * connected.
@@ -70,7 +73,7 @@
      * @hide
      **/
     public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
-            ROUTE_SPEAKER;
+            ROUTE_SPEAKER | ROUTE_EXTERNAL;
 
     private final boolean isMuted;
     private final int route;
@@ -189,7 +192,11 @@
      */
     @CallAudioRoute
     public int getSupportedRouteMask() {
-        return supportedRouteMask;
+        if (route == ROUTE_EXTERNAL) {
+            return ROUTE_EXTERNAL;
+        } else {
+            return supportedRouteMask;
+        }
     }
 
     /**
@@ -233,6 +240,10 @@
             listAppend(buffer, "SPEAKER");
         }
 
+        if ((route & ROUTE_EXTERNAL) == ROUTE_EXTERNAL) {
+            listAppend(buffer, "EXTERNAL");
+        }
+
         return buffer.toString();
     }
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 21a1804..30d4959 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -561,6 +561,15 @@
      */
     public static final int PROPERTY_CROSS_SIM = 1 << 13;
 
+    /**
+     * Connection is a tethered external call.
+     * <p>
+     * Indicates that the {@link Connection} is fixed on this device but the audio streams are
+     * re-routed to another device.
+     * <p>
+     */
+    public static final int PROPERTY_TETHERED_CALL = 1 << 14;
+
     //**********************************************************************************************
     // Next PROPERTY value: 1<<14
     //**********************************************************************************************
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index c8b8ffb..326f417 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -221,7 +221,8 @@
 
     private NetworkScanRequest(Parcel in) {
         mScanType = in.readInt();
-        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
+        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader(),
+                RadioAccessSpecifier.class);
         if (tempSpecifiers != null) {
             mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
             for (int i = 0; i < tempSpecifiers.length; i++) {
diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java
index 74f9c87..464389b 100644
--- a/telephony/java/android/telephony/UiccCardInfo.java
+++ b/telephony/java/android/telephony/UiccCardInfo.java
@@ -156,11 +156,9 @@
     @Nullable
     @Deprecated
     public String getIccId() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mIccIdAccessRestricted) {
-        //    throw new UnsupportedOperationException("getIccId from UiccPortInfo");
-        //}
+        if (mIccIdAccessRestricted) {
+            throw new UnsupportedOperationException("getIccId from UiccPortInfo");
+        }
         //always return ICCID from first port.
         return getPorts().stream().findFirst().get().getIccId();
     }
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index a8668e7..2b1c8c8 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -159,11 +159,9 @@
      */
     @Deprecated
     public boolean getIsActive() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mLogicalSlotAccessRestricted) {
-        //    throw new UnsupportedOperationException("get port status from UiccPortInfo");
-        //}
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get port status from UiccPortInfo");
+        }
         //always return status from first port.
         return getPorts().stream().findFirst().get().isActive();
     }
@@ -198,11 +196,9 @@
      */
     @Deprecated
     public int getLogicalSlotIdx() {
-        // Temporarily bypassing exception
-        // TODO: add exception once refactoring completed.
-        //if (mLogicalSlotAccessRestricted) {
-        //    throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
-        //}
+        if (mLogicalSlotAccessRestricted) {
+            throw new UnsupportedOperationException("get logical slot index from UiccPortInfo");
+        }
         //always return logical slot index from first port.
         //portList always have at least one element.
         return getPorts().stream().findFirst().get().getLogicalSlotIndex();
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 7e2d80e..e6d7df3 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -851,7 +851,8 @@
         mHasKnownUserIntentEmergency = in.readBoolean();
         mRestrictCause = in.readInt();
         mCallerNumberVerificationStatus = in.readInt();
-        Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader());
+        Object[] accepted = in.readArray(RtpHeaderExtensionType.class.getClassLoader(),
+                RtpHeaderExtensionType.class);
         mAcceptedRtpHeaderExtensionTypes = Arrays.stream(accepted)
                 .map(o -> (RtpHeaderExtensionType) o).collect(Collectors.toSet());
     }
diff --git a/tests/AppLaunchWear/Android.bp b/tests/AppLaunchWear/Android.bp
deleted file mode 100644
index e2fc473..0000000
--- a/tests/AppLaunchWear/Android.bp
+++ /dev/null
@@ -1,22 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
-    name: "AppLaunchWear",
-    // Only compile source java files in this apk.
-    srcs: ["src/**/*.java"],
-    platform_apis: true,
-    certificate: "platform",
-    libs: [
-        "android.test.base",
-        "android.test.runner",
-    ],
-    static_libs: ["androidx.test.rules"],
-    test_suites: ["device-tests"],
-}
diff --git a/tests/AppLaunchWear/AndroidManifest.xml b/tests/AppLaunchWear/AndroidManifest.xml
deleted file mode 100644
index 7dfd7ba..0000000
--- a/tests/AppLaunchWear/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.tests.applaunch"
-    android:sharedUserId="android.uid.system" >
-
-   <uses-permission android:name="android.permission.REAL_GET_TASKS" />
-   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
-   <uses-sdk
-        android:minSdkVersion="22"
-        android:targetSdkVersion="24" />
-
-    <instrumentation android:label="Measure app start up time"
-                     android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="com.android.tests.applaunch" />
-
-    <application android:label="App Launch Test">
-        <uses-library android:name="android.test.runner" />
-    </application>
-</manifest>
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
deleted file mode 100644
index 97701c6..0000000
--- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (C) 2013 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.tests.applaunch;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.ActivityManager;
-import android.app.ActivityManager.ProcessErrorStateInfo;
-import android.app.IActivityManager;
-import android.app.UiAutomation;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.test.InstrumentationTestCase;
-import android.test.InstrumentationTestRunner;
-import android.util.Log;
-
-import androidx.test.rule.logging.AtraceLogger;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This test is intended to measure the time it takes for the apps to start.
- * Names of the applications are passed in command line, and the
- * test starts each application, and reports the start up time in milliseconds.
- * The instrumentation expects the following key to be passed on the command line:
- * apps - A list of applications to start and their corresponding result keys
- * in the following format:
- * -e apps <app name>^<result key>|<app name>^<result key>
- */
-public class AppLaunch extends InstrumentationTestCase {
-
-    private static final int JOIN_TIMEOUT = 10000;
-    private static final String TAG = AppLaunch.class.getSimpleName();
-
-    // optional parameter: comma separated list of required account types before proceeding
-    // with the app launch
-    private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
-    private static final String KEY_APPS = "apps";
-    private static final String KEY_TRIAL_LAUNCH = "trial_launch";
-    private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
-    private static final String KEY_LAUNCH_ORDER = "launch_order";
-    private static final String KEY_DROP_CACHE = "drop_cache";
-    private static final String KEY_SIMPLEPERF_CMD = "simpleperf_cmd";
-    private static final String KEY_SIMPLEPERF_APP = "simpleperf_app";
-    private static final String KEY_TRACE_ITERATIONS = "trace_iterations";
-    private static final String KEY_LAUNCH_DIRECTORY = "launch_directory";
-    private static final String KEY_TRACE_DIRECTORY = "trace_directory";
-    private static final String KEY_TRACE_CATEGORY = "trace_categories";
-    private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
-    private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
-    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
-
-    private static final String SIMPLEPERF_APP_CMD =
-            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
-    private static final String WEARABLE_ACTION_GOOGLE =
-            "com.google.android.wearable.action.GOOGLE";
-    private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 5000; // 5s to allow app to idle
-    private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; // 750ms idle for non initial launches
-    private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 5000; // 5s between launching apps
-    private static final String LAUNCH_SUB_DIRECTORY = "launch_logs";
-    private static final String LAUNCH_FILE = "applaunch.txt";
-    private static final String TRACE_SUB_DIRECTORY = "atrace_logs";
-    private static final String DEFAULT_TRACE_CATEGORIES =
-            "sched,freq,gfx,view,dalvik,webview,input,wm,disk,am,wm";
-    private static final String DEFAULT_TRACE_BUFFER_SIZE = "20000";
-    private static final String DEFAULT_TRACE_DUMP_INTERVAL = "10";
-    private static final String TRIAL_LAUNCH = "TRIAL_LAUNCH";
-    private static final String DELIMITER = ",";
-    private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
-    private static final String APP_LAUNCH_CMD = "am start -W -n";
-    private static final String SUCCESS_MESSAGE = "Status: ok";
-    private static final String WARNING_MESSAGE = "Warning: Activity not started";
-    private static final String COMPILE_SUCCESS = "Success";
-    private static final String THIS_TIME = "ThisTime:";
-    private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
-    private static final String TRACE_ITERATION = "TRACE_ITERATION-%d";
-    private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION";
-    private static final String TRACE_ITERATION_PREFIX = "TRACE_ITERATION";
-    private static final String LAUNCH_ORDER_CYCLIC = "cyclic";
-    private static final String LAUNCH_ORDER_SEQUENTIAL = "sequential";
-    private static final String COMPILE_CMD = "cmd package compile -f -m %s %s";
-    private static final String SPEED_PROFILE_FILTER = "speed-profile";
-    private static final String VERIFY_FILTER = "verify";
-    private static final String LAUNCH_SCRIPT_NAME = "appLaunch";
-    private static final String WEARABLE_HOME_PACKAGE = "com.google.android.wearable.app";
-
-    private Map<String, Intent> mNameToIntent;
-    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
-    private Map<String, String> mNameToResultKey;
-    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
-    private IActivityManager mAm;
-    private String mSimplePerfCmd = null;
-    private String mLaunchOrder = null;
-    private boolean mDropCache = false;
-    private int mLaunchIterations = 10;
-    private int mTraceLaunchCount = 0;
-    private String mTraceDirectoryStr = null;
-    private Bundle mResult = new Bundle();
-    private Set<String> mRequiredAccounts;
-    private boolean mTrialLaunch = false;
-    private BufferedWriter mBufferedWriter = null;
-    private boolean mSimplePerfAppOnly = false;
-    private String[] mCompilerFilters = null;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
-        super.tearDown();
-    }
-
-    private void addLaunchResult(LaunchOrder launch, AppLaunchResult result) {
-        mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter()).add(result);
-    }
-
-    private boolean hasFailureOnFirstLaunch(LaunchOrder launch) {
-        List<AppLaunchResult> results =
-            mNameToLaunchTime.get(launch.getApp()).get(launch.getCompilerFilter());
-        return (results.size() > 0) && (results.get(0).mLaunchTime < 0);
-    }
-
-    public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException,
-            IOException, InterruptedException {
-        InstrumentationTestRunner instrumentation =
-                (InstrumentationTestRunner)getInstrumentation();
-        Bundle args = instrumentation.getArguments();
-        mAm = ActivityManager.getService();
-        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
-
-        createMappings();
-        parseArgs(args);
-        checkAccountSignIn();
-
-        // Root directory for applaunch file to log the app launch output
-        // Will be useful in case of simpleperf command is used
-        File launchRootDir = null;
-        if (null != launchDirectory && !launchDirectory.isEmpty()) {
-            launchRootDir = new File(launchDirectory);
-            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
-                throw new IOException("Unable to create the destination directory");
-            }
-        }
-
-        try {
-            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
-
-            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
-                throw new IOException("Unable to create the lauch file sub directory");
-            }
-            File file = new File(launchSubDir, LAUNCH_FILE);
-            FileOutputStream outputStream = new FileOutputStream(file);
-            mBufferedWriter = new BufferedWriter(new OutputStreamWriter(
-                    outputStream));
-
-            // Root directory for trace file during the launches
-            File rootTrace = null;
-            File rootTraceSubDir = null;
-            int traceBufferSize = 0;
-            int traceDumpInterval = 0;
-            Set<String> traceCategoriesSet = null;
-            if (null != mTraceDirectoryStr && !mTraceDirectoryStr.isEmpty()) {
-                rootTrace = new File(mTraceDirectoryStr);
-                if (!rootTrace.exists() && !rootTrace.mkdirs()) {
-                    throw new IOException("Unable to create the trace directory");
-                }
-                rootTraceSubDir = new File(rootTrace, TRACE_SUB_DIRECTORY);
-                if (!rootTraceSubDir.exists() && !rootTraceSubDir.mkdirs()) {
-                    throw new IOException("Unable to create the trace sub directory");
-                }
-                assertNotNull("Trace iteration parameter is mandatory",
-                        args.getString(KEY_TRACE_ITERATIONS));
-                mTraceLaunchCount = Integer.parseInt(args.getString(KEY_TRACE_ITERATIONS));
-                String traceCategoriesStr = args
-                        .getString(KEY_TRACE_CATEGORY, DEFAULT_TRACE_CATEGORIES);
-                traceBufferSize = Integer.parseInt(args.getString(KEY_TRACE_BUFFERSIZE,
-                        DEFAULT_TRACE_BUFFER_SIZE));
-                traceDumpInterval = Integer.parseInt(args.getString(KEY_TRACE_DUMPINTERVAL,
-                        DEFAULT_TRACE_DUMP_INTERVAL));
-                traceCategoriesSet = new HashSet<String>();
-                if (!traceCategoriesStr.isEmpty()) {
-                    String[] traceCategoriesSplit = traceCategoriesStr.split(DELIMITER);
-                    for (int i = 0; i < traceCategoriesSplit.length; i++) {
-                        traceCategoriesSet.add(traceCategoriesSplit[i]);
-                    }
-                }
-            }
-
-            // Get the app launch order based on launch order, trial launch,
-            // launch iterations and trace iterations
-            setLaunchOrder();
-
-            for (LaunchOrder launch : mLaunchOrderList) {
-                if (mNameToIntent.get(launch.getApp()) == null) {
-                    continue;
-                }
-                dropCache();
-                String appPkgName = mNameToIntent.get(launch.getApp())
-                        .getComponent().getPackageName();
-                Log.v(TAG, String.format("\nApp name: %s", launch.getApp()));
-                Log.v(TAG, String.format("Adding app package name: %s", appPkgName));
-                // App launch times for trial launch will not be used for final
-                // launch time calculations.
-                if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
-                    // In the "applaunch.txt" file, trail launches is referenced using
-                    // "TRIAL_LAUNCH"
-                    Log.v(TAG, "Trial Launch");
-                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(VERIFY_FILTER, appPkgName));
-                    } else if (launch.getCompilerFilter() != null) {
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(launch.getCompilerFilter(), appPkgName));
-                    }
-                    // We only need to run a trial for the speed-profile filter, but we always
-                    // run one for "applaunch.txt" consistency.
-                    AppLaunchResult launchResult = null;
-                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                        Log.v(TAG, "Home package detected. Not killing app");
-                        launchResult = startApp(launch.getApp(), false, launch.getLaunchReason());
-                    } else {
-                        Log.v(TAG, "Will kill app before launch");
-                        launchResult = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    }
-                    if (launchResult.mLaunchTime < 0) {
-                        addLaunchResult(launch, new AppLaunchResult());
-                        // simply pass the app if launch isn't successful
-                        // error should have already been logged by startApp
-                        continue;
-                    }
-                    sleep(INITIAL_LAUNCH_IDLE_TIMEOUT);
-                    if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
-                        // Send SIGUSR1 to force dumping a profile.
-                        String sendSignalCommand =
-                            String.format("killall -s SIGUSR1 %s", appPkgName);
-                        getInstrumentation().getUiAutomation().executeShellCommand(
-                            sendSignalCommand);
-                        assertTrue(String.format("Not able to compile the app : %s", appPkgName),
-                              compileApp(launch.getCompilerFilter(), appPkgName));
-                    }
-                }
-
-                // App launch times used for final calculation
-                else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
-                    Log.v(TAG, "Launch iteration prefix.");
-                    AppLaunchResult launchResults = null;
-                    if (hasFailureOnFirstLaunch(launch)) {
-                        // skip if the app has failures while launched first
-                        continue;
-                    }
-                    // In the "applaunch.txt" file app launches are referenced using
-                    // "LAUNCH_ITERATION - ITERATION NUM"
-                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                        Log.v(TAG, "Home package detected. Not killing app");
-                        launchResults = startApp(launch.getApp(), false, launch.getLaunchReason());
-                    } else {
-                        Log.v(TAG, "Will kill app before launch");
-                        launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
-                    }
-                    if (launchResults.mLaunchTime < 0) {
-                        addLaunchResult(launch, new AppLaunchResult());
-                        // if it fails once, skip the rest of the launches
-                        continue;
-                    } else {
-                        addLaunchResult(launch, launchResults);
-                    }
-                    sleep(POST_LAUNCH_IDLE_TIMEOUT);
-                }
-
-                // App launch times for trace launch will not be used for final
-                // launch time calculations.
-                else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
-                    Log.v(TAG, "Trace iteration prefix");
-                    AtraceLogger atraceLogger = AtraceLogger
-                            .getAtraceLoggerInstance(getInstrumentation());
-                    // Start the trace
-                    try {
-                        atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
-                                traceDumpInterval, rootTraceSubDir,
-                                String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
-                        if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
-                            Log.v(TAG, "Home package detected. Not killing app");
-                            startApp(launch.getApp(), false, launch.getLaunchReason());
-                        } else {
-                            Log.v(TAG, "Will kill app before launch");
-                            startApp(launch.getApp(), true, launch.getLaunchReason());
-                        }
-                        sleep(POST_LAUNCH_IDLE_TIMEOUT);
-                    } finally {
-                        // Stop the trace
-                        atraceLogger.atraceStop();
-                    }
-                }
-                closeApp(launch.getApp(), true);
-                sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
-            }
-        } finally {
-            if (null != mBufferedWriter) {
-                mBufferedWriter.close();
-            }
-        }
-
-        for (String app : mNameToResultKey.keySet()) {
-            for (String compilerFilter : mCompilerFilters) {
-                StringBuilder launchTimes = new StringBuilder();
-                StringBuilder cpuCycles = new StringBuilder();
-                StringBuilder majorFaults = new StringBuilder();
-                for (AppLaunchResult result : mNameToLaunchTime.get(app).get(compilerFilter)) {
-                    launchTimes.append(result.mLaunchTime);
-                    launchTimes.append(",");
-                    if (mSimplePerfAppOnly) {
-                        cpuCycles.append(result.mCpuCycles);
-                        cpuCycles.append(",");
-                        majorFaults.append(result.mMajorFaults);
-                        majorFaults.append(",");
-                    }
-                }
-                String filterName = (compilerFilter == null) ? "" : ("-" + compilerFilter);
-                mResult.putString(mNameToResultKey.get(app) + filterName, launchTimes.toString());
-                if (mSimplePerfAppOnly) {
-                    mResult.putString(mNameToResultKey.get(app) + filterName + "-cpuCycles",
-                        cpuCycles.toString());
-                    mResult.putString(mNameToResultKey.get(app) + filterName + "-majorFaults",
-                        majorFaults.toString());
-                }
-            }
-        }
-        instrumentation.sendStatus(0, mResult);
-    }
-
-    /**
-     * Compile the app package using compilerFilter and return true or false
-     * based on status of the compilation command.
-     */
-    private boolean compileApp(String compilerFilter, String appPkgName) throws IOException {
-        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
-                executeShellCommand(String.format(COMPILE_CMD, compilerFilter, appPkgName));
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
-                        new FileInputStream(result.getFileDescriptor())))) {
-            String line;
-            while ((line = bufferedReader.readLine()) != null) {
-                if (line.contains(COMPILE_SUCCESS)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-
-    /**
-     * If launch order is "cyclic" then apps will be launched one after the
-     * other for each iteration count.
-     * If launch order is "sequential" then each app will be launched for given number
-     * iterations at once before launching the other apps.
-     */
-    private void setLaunchOrder() {
-        if (LAUNCH_ORDER_CYCLIC.equalsIgnoreCase(mLaunchOrder)) {
-            for (String compilerFilter : mCompilerFilters) {
-                if (mTrialLaunch) {
-                    for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
-                    }
-                }
-                for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                    for (String app : mNameToResultKey.keySet()) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                  String.format(LAUNCH_ITERATION, launchCount)));
-                    }
-                }
-                if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                    for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
-                        for (String app : mNameToResultKey.keySet()) {
-                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                      String.format(TRACE_ITERATION, traceCount)));
-                        }
-                    }
-                }
-            }
-        } else if (LAUNCH_ORDER_SEQUENTIAL.equalsIgnoreCase(mLaunchOrder)) {
-            for (String compilerFilter : mCompilerFilters) {
-                for (String app : mNameToResultKey.keySet()) {
-                    if (mTrialLaunch) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH));
-                    }
-                    for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
-                        mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                String.format(LAUNCH_ITERATION, launchCount)));
-                    }
-                    if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
-                        for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
-                            mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                    String.format(TRACE_ITERATION, traceCount)));
-                        }
-                    }
-                }
-            }
-        } else {
-            assertTrue("Launch order is not valid parameter", false);
-        }
-    }
-
-    private void dropCache() {
-        if (mDropCache) {
-            assertNotNull("Issue in dropping the cache",
-                    getInstrumentation().getUiAutomation()
-                            .executeShellCommand(DROP_CACHE_SCRIPT));
-        }
-    }
-
-    private void parseArgs(Bundle args) {
-        mNameToResultKey = new LinkedHashMap<String, String>();
-        mNameToLaunchTime = new HashMap<>();
-        String launchIterations = args.getString(KEY_LAUNCH_ITERATIONS);
-        if (launchIterations != null) {
-            mLaunchIterations = Integer.parseInt(launchIterations);
-        }
-        String appList = args.getString(KEY_APPS);
-        if (appList == null)
-            return;
-
-        String appNames[] = appList.split("\\|");
-        for (String pair : appNames) {
-            String[] parts = pair.split("\\^");
-            if (parts.length != 2) {
-                Log.e(TAG, "The apps key is incorrectly formatted");
-                fail();
-            }
-
-            mNameToResultKey.put(parts[0], parts[1]);
-            mNameToLaunchTime.put(parts[0], null);
-        }
-        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
-        if (requiredAccounts != null) {
-            mRequiredAccounts = new HashSet<String>();
-            for (String accountType : requiredAccounts.split(",")) {
-                mRequiredAccounts.add(accountType);
-            }
-        }
-
-        String compilerFilterList = args.getString(KEY_COMPILER_FILTERS);
-        if (compilerFilterList != null) {
-            // If a compiler filter is passed, we make a trial launch to force compilation
-            // of the apps.
-            mTrialLaunch = true;
-            mCompilerFilters = compilerFilterList.split("\\|");
-        } else {
-            // Just pass a null compiler filter to use the current state of the app.
-            mCompilerFilters = new String[1];
-        }
-
-        // Pre-populate the results map to avoid null checks.
-        for (String app : mNameToLaunchTime.keySet()) {
-            HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
-            mNameToLaunchTime.put(app, map);
-            for (String compilerFilter : mCompilerFilters) {
-                map.put(compilerFilter, new ArrayList<>());
-            }
-        }
-
-        mTraceDirectoryStr = args.getString(KEY_TRACE_DIRECTORY);
-        mDropCache = Boolean.parseBoolean(args.getString(KEY_DROP_CACHE));
-        mSimplePerfCmd = args.getString(KEY_SIMPLEPERF_CMD);
-        mLaunchOrder = args.getString(KEY_LAUNCH_ORDER, LAUNCH_ORDER_CYCLIC);
-        mSimplePerfAppOnly = Boolean.parseBoolean(args.getString(KEY_SIMPLEPERF_APP));
-        mTrialLaunch = mTrialLaunch || Boolean.parseBoolean(args.getString(KEY_TRIAL_LAUNCH));
-
-        if (mSimplePerfCmd != null && mSimplePerfAppOnly) {
-            Log.w(TAG, String.format("Passing both %s and %s is not supported, ignoring %s",
-                KEY_SIMPLEPERF_CMD, KEY_SIMPLEPERF_APP));
-        }
-    }
-
-    private boolean hasLeanback(Context context) {
-        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
-    }
-
-    private void createMappings() {
-        mNameToIntent = new LinkedHashMap<String, Intent>();
-
-        PackageManager pm = getInstrumentation().getContext()
-                .getPackageManager();
-        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
-        intentToResolve.addCategory(hasLeanback(getInstrumentation().getContext()) ?
-                Intent.CATEGORY_LEANBACK_LAUNCHER :
-                Intent.CATEGORY_LAUNCHER);
-        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);
-        resolveLoop(ris, intentToResolve, pm);
-        // For Wear
-        intentToResolve = new Intent(WEARABLE_ACTION_GOOGLE);
-        ris = pm.queryIntentActivities(intentToResolve, 0);
-        resolveLoop(ris, intentToResolve, pm);
-    }
-
-    private void resolveLoop(List<ResolveInfo> ris, Intent intentToResolve, PackageManager pm) {
-        if (ris == null || ris.isEmpty()) {
-            Log.i(TAG, "Could not find any apps");
-        } else {
-            for (ResolveInfo ri : ris) {
-                Intent startIntent = new Intent(intentToResolve);
-                startIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-                startIntent.setClassName(ri.activityInfo.packageName,
-                        ri.activityInfo.name);
-                String appName = ri.loadLabel(pm).toString();
-                if (appName != null) {
-                    // Support launching intent using package name or app name
-                    mNameToIntent.put(ri.activityInfo.packageName, startIntent);
-                    mNameToIntent.put(appName, startIntent);
-                }
-            }
-        }
-    }
-
-    private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
-            String launchReason) throws NameNotFoundException, RemoteException {
-        Log.i(TAG, "Starting " + appName);
-
-        Intent startIntent = mNameToIntent.get(appName);
-        if (startIntent == null) {
-            Log.w(TAG, "App does not exist: " + appName);
-            mResult.putString(mNameToResultKey.get(appName), "App does not exist");
-            return new AppLaunchResult();
-        }
-        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
-                launchReason);
-        Thread t = new Thread(runnable);
-        t.start();
-        try {
-            t.join(JOIN_TIMEOUT);
-        } catch (InterruptedException e) {
-            // ignore
-        }
-        return runnable.getResult();
-    }
-
-    private void checkAccountSignIn() {
-        // ensure that the device has the required account types before starting test
-        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
-        // for Gmail
-        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
-            return;
-        }
-        final AccountManager am =
-                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
-                        Context.ACCOUNT_SERVICE);
-        Account[] accounts = am.getAccounts();
-        // use set here in case device has multiple accounts of the same type
-        Set<String> foundAccounts = new HashSet<String>();
-        for (Account account : accounts) {
-            if (mRequiredAccounts.contains(account.type)) {
-                foundAccounts.add(account.type);
-            }
-        }
-        // check if account type matches, if not, fail test with message on what account types
-        // are missing
-        if (mRequiredAccounts.size() != foundAccounts.size()) {
-            mRequiredAccounts.removeAll(foundAccounts);
-            StringBuilder sb = new StringBuilder("Device missing these accounts:");
-            for (String account : mRequiredAccounts) {
-                sb.append(' ');
-                sb.append(account);
-            }
-            fail(sb.toString());
-        }
-    }
-
-    private void closeApp(String appName, boolean forceStopApp) {
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN);
-        homeIntent.addCategory(Intent.CATEGORY_HOME);
-        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-        getInstrumentation().getContext().startActivity(homeIntent);
-        sleep(POST_LAUNCH_IDLE_TIMEOUT);
-        if (forceStopApp) {
-            Intent startIntent = mNameToIntent.get(appName);
-            if (startIntent != null) {
-                String packageName = startIntent.getComponent().getPackageName();
-                try {
-                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Error closing app", e);
-                }
-            }
-        }
-    }
-
-    private void sleep(int time) {
-        try {
-            Thread.sleep(time);
-        } catch (InterruptedException e) {
-            // ignore
-        }
-    }
-
-    private void reportError(String appName, String processName) {
-        ActivityManager am = (ActivityManager) getInstrumentation()
-                .getContext().getSystemService(Context.ACTIVITY_SERVICE);
-        List<ProcessErrorStateInfo> crashes = am.getProcessesInErrorState();
-        if (crashes != null) {
-            for (ProcessErrorStateInfo crash : crashes) {
-                if (!crash.processName.equals(processName))
-                    continue;
-
-                Log.w(TAG, appName + " crashed: " + crash.shortMsg);
-                mResult.putString(mNameToResultKey.get(appName), crash.shortMsg);
-                return;
-            }
-        }
-
-        mResult.putString(mNameToResultKey.get(appName),
-                "Crashed for unknown reason");
-        Log.w(TAG, appName
-                + " not found in process list, most likely it is crashed");
-    }
-
-    private class LaunchOrder {
-        private String mApp;
-        private String mCompilerFilter;
-        private String mLaunchReason;
-
-        LaunchOrder(String app, String compilerFilter, String launchReason){
-            mApp = app;
-            mCompilerFilter = compilerFilter;
-            mLaunchReason = launchReason;
-        }
-
-        public String getApp() {
-            return mApp;
-        }
-
-        public void setApp(String app) {
-            mApp = app;
-        }
-
-        public String getCompilerFilter() {
-            return mCompilerFilter;
-        }
-
-        public String getLaunchReason() {
-            return mLaunchReason;
-        }
-
-        public void setLaunchReason(String launchReason) {
-            mLaunchReason = launchReason;
-        }
-    }
-
-    private class AppLaunchResult {
-        long mLaunchTime;
-        long mCpuCycles;
-        long mMajorFaults;
-
-        AppLaunchResult() {
-            mLaunchTime = -1L;
-            mCpuCycles = -1L;
-            mMajorFaults = -1L;
-        }
-
-        AppLaunchResult(String launchTime, String cpuCycles, String majorFaults) {
-            try {
-                mLaunchTime = Long.parseLong(launchTime, 10);
-                mCpuCycles = Long.parseLong(cpuCycles, 10);
-                mMajorFaults = Long.parseLong(majorFaults, 10);
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "Error parsing result", e);
-            }
-        }
-    }
-
-    private class AppLaunchRunnable implements Runnable {
-        private Intent mLaunchIntent;
-        private AppLaunchResult mLaunchResult;
-        private boolean mForceStopBeforeLaunch;
-        private String mLaunchReason;
-
-        public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
-                String launchReason) {
-            mLaunchIntent = intent;
-            mForceStopBeforeLaunch = forceStopBeforeLaunch;
-            mLaunchReason = launchReason;
-            mLaunchResult = new AppLaunchResult();
-        }
-
-        public AppLaunchResult getResult() {
-            return mLaunchResult;
-        }
-
-        public void run() {
-            File launchFile = null;
-            try {
-                String packageName = mLaunchIntent.getComponent().getPackageName();
-                String componentName = mLaunchIntent.getComponent().flattenToShortString();
-                if (mForceStopBeforeLaunch) {
-                    Log.v(TAG, "Stopping app before launch");
-                    mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
-                } else {
-                    Log.v(TAG, "Not killing app. Going to Home Screen.");
-                    ParcelFileDescriptor goHome = getInstrumentation().getUiAutomation()
-                        .executeShellCommand("input keyevent 3");
-                }
-                String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
-                if (mSimplePerfAppOnly) {
-                    try {
-                        // executeShellCommand cannot handle shell specific actions, like '&'.
-                        // Therefore, we create a file containing the command and make that
-                        // the command to launch.
-                        launchFile = File.createTempFile(LAUNCH_SCRIPT_NAME, ".sh");
-                        launchFile.setExecutable(true);
-                        try (FileOutputStream stream = new FileOutputStream(launchFile);
-                             BufferedWriter writer =
-                                new BufferedWriter(new OutputStreamWriter(stream))) {
-                            String cmd = String.format(SIMPLEPERF_APP_CMD, packageName, launchCmd);
-                            writer.write(cmd);
-                        }
-                        launchCmd = launchFile.getAbsolutePath();
-                    } catch (IOException e) {
-                        Log.w(TAG, "Error writing the launch command", e);
-                        return;
-                    }
-                } else if (null != mSimplePerfCmd) {
-                    launchCmd = String.format("%s %s", mSimplePerfCmd, launchCmd);
-                }
-                Log.v(TAG, "Final launch cmd:" + launchCmd);
-                ParcelFileDescriptor parcelDesc = getInstrumentation().getUiAutomation()
-                        .executeShellCommand(launchCmd);
-                mLaunchResult = parseLaunchTimeAndWrite(parcelDesc, String.format
-                        ("App Launch :%s %s", componentName, mLaunchReason));
-            } catch (RemoteException e) {
-                Log.w(TAG, "Error launching app", e);
-            } finally {
-                if (launchFile != null) {
-                    launchFile.delete();
-                }
-            }
-        }
-
-        /**
-         * Method to parse the launch time info and write the result to file
-         *
-         * @param parcelDesc
-         * @return
-         */
-        private AppLaunchResult parseLaunchTimeAndWrite(ParcelFileDescriptor parcelDesc,
-                String headerInfo) {
-            String launchTime = "-1";
-            String cpuCycles = "-1";
-            String majorFaults = "-1";
-            boolean launchSuccess = false;
-            try {
-                InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
-                /* SAMPLE OUTPUT :
-                Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
-                Status: ok
-                Activity: com.google.android.calculator/com.android.calculator2.Calculator
-                ThisTime: 357
-                TotalTime: 357
-                WaitTime: 377
-                Complete*/
-                /* WHEN NOT KILLING HOME :
-                Starting: Intent { cmp=com.google.android.wearable.app/
-                    com.google.android.clockwork.home.calendar.AgendaActivity }
-                Warning: Activity not started, its current task has been brought to the front
-                Status: ok
-                Activity: com.google.android.wearable.app/
-                    com.google.android.clockwork.home.calendar.AgendaActivity
-                ThisTime: 209
-                TotalTime: 209
-                WaitTime: 285
-                Complete*/
-                /* WITH SIMPLEPERF :
-                Performance counter statistics,
-                6595722690,cpu-cycles,4.511040,GHz,(100%),
-                0,major-faults,0.000,/sec,(100%),
-                Total test time,1.462129,seconds,*/
-                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
-                        inputStream));
-                String line = null;
-                int lineCount = 1;
-                int addLineForWarning = 0;
-                mBufferedWriter.newLine();
-                mBufferedWriter.write(headerInfo);
-                mBufferedWriter.newLine();
-                while ((line = bufferedReader.readLine()) != null) {
-                    if (lineCount == 2 && line.contains(WARNING_MESSAGE)) {
-                        addLineForWarning = 1;
-                    }
-                    if (lineCount == (2 + addLineForWarning) && line.contains(SUCCESS_MESSAGE)) {
-                        launchSuccess = true;
-                    }
-                    // Parse TotalTime which is the launch time
-                    if (launchSuccess && lineCount == (5 + addLineForWarning)) {
-                        String launchSplit[] = line.split(":");
-                        launchTime = launchSplit[1].trim();
-                    }
-
-                    if (mSimplePerfAppOnly) {
-                        // Parse simpleperf output.
-                        if (lineCount == (9 + addLineForWarning)) {
-                            if (!line.contains("cpu-cycles")) {
-                                Log.e(TAG, "Error in simpleperf output");
-                            } else {
-                                cpuCycles = line.split(",")[0].trim();
-                            }
-                        } else if (lineCount == (10 + addLineForWarning)) {
-                            if (!line.contains("major-faults")) {
-                                Log.e(TAG, "Error in simpleperf output");
-                            } else {
-                                majorFaults = line.split(",")[0].trim();
-                            }
-                        }
-                    }
-                    mBufferedWriter.write(line);
-                    mBufferedWriter.newLine();
-                    lineCount++;
-                }
-                mBufferedWriter.flush();
-                inputStream.close();
-            } catch (IOException e) {
-                Log.w(TAG, "Error writing the launch file", e);
-            }
-            return new AppLaunchResult(launchTime, cpuCycles, majorFaults);
-        }
-
-    }
-}
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index d03aee2..4a724b7 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -15,12 +15,13 @@
  */
 package android.net.vcn;
 
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 
 import org.junit.Test;
 
@@ -32,26 +33,26 @@
     private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
 
     // Package private for use in VcnGatewayConnectionConfigTest
-    static VcnCellUnderlyingNetworkTemplate getTestNetworkPriority() {
+    static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnCellUnderlyingNetworkTemplate.Builder()
                 .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setAllowedOperatorPlmnIds(ALLOWED_PLMN_IDS)
-                .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
-                .setAllowRoaming(true /* allowRoaming */)
-                .setRequireOpportunistic(true /* requireOpportunistic */)
+                .setMetered(MATCH_FORBIDDEN)
+                .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
+                .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+                .setRoaming(MATCH_FORBIDDEN)
+                .setOpportunistic(MATCH_REQUIRED)
                 .build();
     }
 
     @Test
     public void testBuilderAndGetters() {
-        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
-        assertTrue(networkPriority.allowMetered());
-        assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedOperatorPlmnIds());
-        assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
-        assertTrue(networkPriority.allowRoaming());
-        assertTrue(networkPriority.requireOpportunistic());
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
+        assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
+        assertEquals(MATCH_REQUIRED, networkPriority.getOpportunistic());
     }
 
     @Test
@@ -59,16 +60,16 @@
         final VcnCellUnderlyingNetworkTemplate networkPriority =
                 new VcnCellUnderlyingNetworkTemplate.Builder().build();
         assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
-        assertFalse(networkPriority.allowMetered());
-        assertEquals(new HashSet<String>(), networkPriority.getAllowedOperatorPlmnIds());
-        assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
-        assertFalse(networkPriority.allowRoaming());
-        assertFalse(networkPriority.requireOpportunistic());
+        assertEquals(MATCH_ANY, networkPriority.getMetered());
+        assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
+        assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
+        assertEquals(MATCH_ANY, networkPriority.getRoaming());
+        assertEquals(MATCH_ANY, networkPriority.getOpportunistic());
     }
 
     @Test
     public void testPersistableBundle() {
-        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+        final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(
                 networkPriority,
                 VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 1f2905d..2aef9ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -17,8 +17,8 @@
 package android.net.vcn;
 
 import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
-import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES;
-import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_PRIORITIES_KEY;
+import static android.net.vcn.VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES;
+import static android.net.vcn.VcnGatewayConnectionConfig.UNDERLYING_NETWORK_TEMPLATES_KEY;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -40,8 +40,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -54,17 +55,17 @@
             };
     public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
 
-    private static final LinkedHashSet<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_PRIORITIES =
-            new LinkedHashSet();
+    private static final List<VcnUnderlyingNetworkTemplate> UNDERLYING_NETWORK_TEMPLATES =
+            new ArrayList();
 
     static {
         Arrays.sort(EXPOSED_CAPS);
         Arrays.sort(UNDERLYING_CAPS);
 
-        UNDERLYING_NETWORK_PRIORITIES.add(
-                VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
-        UNDERLYING_NETWORK_PRIORITIES.add(
-                VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+        UNDERLYING_NETWORK_TEMPLATES.add(
+                VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        UNDERLYING_NETWORK_TEMPLATES.add(
+                VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
     }
 
     public static final long[] RETRY_INTERVALS_MS =
@@ -95,7 +96,7 @@
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
         final VcnGatewayConnectionConfig.Builder builder =
-                newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+                newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_TEMPLATES);
 
         return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
     }
@@ -174,10 +175,10 @@
     }
 
     @Test
-    public void testBuilderRequiresNonNullNetworkPriorities() {
+    public void testBuilderRequiresNonNullNetworkTemplates() {
         try {
             newBuilder().setVcnUnderlyingNetworkPriorities(null);
-            fail("Expected exception due to invalid underlyingNetworkPriorities");
+            fail("Expected exception due to invalid underlyingNetworkTemplates");
         } catch (NullPointerException e) {
         }
     }
@@ -219,7 +220,7 @@
         Arrays.sort(exposedCaps);
         assertArrayEquals(EXPOSED_CAPS, exposedCaps);
 
-        assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+        assertEquals(UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
         assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
 
         assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
@@ -234,13 +235,13 @@
     }
 
     @Test
-    public void testParsePersistableBundleWithoutVcnUnderlyingNetworkPriorities() {
+    public void testParsePersistableBundleWithoutVcnUnderlyingNetworkTemplates() {
         PersistableBundle configBundle = buildTestConfig().toPersistableBundle();
-        configBundle.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, null);
+        configBundle.putPersistableBundle(UNDERLYING_NETWORK_TEMPLATES_KEY, null);
 
         final VcnGatewayConnectionConfig config = new VcnGatewayConnectionConfig(configBundle);
         assertEquals(
-                DEFAULT_UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
+                DEFAULT_UNDERLYING_NETWORK_TEMPLATES, config.getVcnUnderlyingNetworkPriorities());
     }
 
     private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
@@ -285,39 +286,36 @@
         assertNotEquals(config, anotherConfig);
     }
 
-    private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkPriorities(
-            LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPriorities) {
+    private static VcnGatewayConnectionConfig buildTestConfigWithVcnUnderlyingNetworkTemplates(
+            List<VcnUnderlyingNetworkTemplate> networkTemplates) {
         return buildTestConfigWithExposedCaps(
                 new VcnGatewayConnectionConfig.Builder(
-                                "buildTestConfigWithVcnUnderlyingNetworkPriorities",
+                                "buildTestConfigWithVcnUnderlyingNetworkTemplates",
                                 TUNNEL_CONNECTION_PARAMS)
-                        .setVcnUnderlyingNetworkPriorities(networkPriorities),
+                        .setVcnUnderlyingNetworkPriorities(networkTemplates),
                 EXPOSED_CAPS);
     }
 
     @Test
-    public void testVcnUnderlyingNetworkPrioritiesEquality() throws Exception {
+    public void testVcnUnderlyingNetworkTemplatesEquality() throws Exception {
         final VcnGatewayConnectionConfig config =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(UNDERLYING_NETWORK_TEMPLATES);
 
-        final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesEqual =
-                new LinkedHashSet();
-        networkPrioritiesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkPriority());
-        networkPrioritiesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+        final List<VcnUnderlyingNetworkTemplate> networkTemplatesEqual = new ArrayList();
+        networkTemplatesEqual.add(VcnCellUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
+        networkTemplatesEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
         final VcnGatewayConnectionConfig configEqual =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesEqual);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesEqual);
 
-        final LinkedHashSet<VcnUnderlyingNetworkTemplate> networkPrioritiesNotEqual =
-                new LinkedHashSet();
-        networkPrioritiesNotEqual.add(
-                VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkPriority());
+        final List<VcnUnderlyingNetworkTemplate> networkTemplatesNotEqual = new ArrayList();
+        networkTemplatesNotEqual.add(VcnWifiUnderlyingNetworkTemplateTest.getTestNetworkTemplate());
         final VcnGatewayConnectionConfig configNotEqual =
-                buildTestConfigWithVcnUnderlyingNetworkPriorities(networkPrioritiesNotEqual);
+                buildTestConfigWithVcnUnderlyingNetworkTemplates(networkTemplatesNotEqual);
 
-        assertEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesEqual);
+        assertEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesEqual);
         assertEquals(config, configEqual);
 
-        assertNotEquals(UNDERLYING_NETWORK_PRIORITIES, networkPrioritiesNotEqual);
+        assertNotEquals(UNDERLYING_NETWORK_TEMPLATES, networkTemplatesNotEqual);
         assertNotEquals(config, configNotEqual);
     }
 }
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index 652057f..cb5b47b 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -15,36 +15,38 @@
  */
 package android.net.vcn;
 
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.junit.Test;
 
+import java.util.Set;
+
 public class VcnWifiUnderlyingNetworkTemplateTest {
     private static final String SSID = "TestWifi";
     private static final int INVALID_NETWORK_QUALITY = -1;
 
     // Package private for use in VcnGatewayConnectionConfigTest
-    static VcnWifiUnderlyingNetworkTemplate getTestNetworkPriority() {
+    static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnWifiUnderlyingNetworkTemplate.Builder()
                 .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setSsid(SSID)
+                .setMetered(MATCH_FORBIDDEN)
+                .setSsids(Set.of(SSID))
                 .build();
     }
 
     @Test
     public void testBuilderAndGetters() {
-        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
-        assertTrue(networkPriority.allowMetered());
-        assertEquals(SSID, networkPriority.getSsid());
+        assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(Set.of(SSID), networkPriority.getSsids());
     }
 
     @Test
@@ -52,8 +54,8 @@
         final VcnWifiUnderlyingNetworkTemplate networkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder().build();
         assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
-        assertFalse(networkPriority.allowMetered());
-        assertNull(SSID, networkPriority.getSsid());
+        assertEquals(MATCH_ANY, networkPriority.getMetered());
+        assertTrue(networkPriority.getSsids().isEmpty());
     }
 
     @Test
@@ -68,7 +70,7 @@
 
     @Test
     public void testPersistableBundle() {
-        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkPriority();
+        final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(
                 networkPriority,
                 VcnUnderlyingNetworkTemplate.fromPersistableBundle(
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index f23d5bf..4bb7de8 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn.routeselection;
 
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
+import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK;
 
 import static com.android.server.vcn.VcnTestUtils.setupSystemService;
@@ -145,7 +147,7 @@
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(false /* allowMetered */)
+                        .setMetered(MATCH_FORBIDDEN)
                         .build();
 
         assertFalse(
@@ -164,7 +166,6 @@
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
                         .build();
         final UnderlyingNetworkRecord selectedNetworkRecord =
                 isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -214,8 +215,7 @@
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
                         .setNetworkQuality(NETWORK_QUALITY_OK)
-                        .setAllowMetered(true /* allowMetered */)
-                        .setSsid(nwPrioritySsid)
+                        .setSsids(Set.of(nwPrioritySsid))
                         .build();
 
         assertEquals(
@@ -238,10 +238,7 @@
     }
 
     private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
-        return new VcnCellUnderlyingNetworkTemplate.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
-                .setAllowMetered(true /* allowMetered */)
-                .setAllowRoaming(true /* allowRoaming */);
+        return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK);
     }
 
     @Test
@@ -258,9 +255,7 @@
     @Test
     public void testMatchOpportunisticCell() {
         final VcnCellUnderlyingNetworkTemplate opportunisticCellNetworkPriority =
-                getCellNetworkPriorityBuilder()
-                        .setRequireOpportunistic(true /* requireOpportunistic */)
-                        .build();
+                getCellNetworkPriorityBuilder().setOpportunistic(MATCH_REQUIRED).build();
 
         when(mSubscriptionSnapshot.isOpportunistic(SUB_ID)).thenReturn(true);
         when(mSubscriptionSnapshot.getAllSubIdsInGroup(SUB_GROUP)).thenReturn(new ArraySet<>());
@@ -279,7 +274,7 @@
         final String networkPriorityPlmnId = useMatchedPlmnId ? PLMN_ID : PLMN_ID_OTHER;
         final VcnCellUnderlyingNetworkTemplate networkPriority =
                 getCellNetworkPriorityBuilder()
-                        .setAllowedOperatorPlmnIds(Set.of(networkPriorityPlmnId))
+                        .setOperatorPlmnIds(Set.of(networkPriorityPlmnId))
                         .build();
 
         assertEquals(
@@ -308,7 +303,7 @@
         final int networkPriorityCarrierId = useMatchedCarrierId ? CARRIER_ID : CARRIER_ID_OTHER;
         final VcnCellUnderlyingNetworkTemplate networkPriority =
                 getCellNetworkPriorityBuilder()
-                        .setAllowedSpecificCarrierIds(Set.of(networkPriorityCarrierId))
+                        .setSimSpecificCarrierIds(Set.of(networkPriorityCarrierId))
                         .build();
 
         assertEquals(
@@ -336,7 +331,7 @@
     @Test
     public void testMatchWifiFailWithoutNotRoamingBit() {
         final VcnCellUnderlyingNetworkTemplate networkPriority =
-                getCellNetworkPriorityBuilder().setAllowRoaming(false /* allowRoaming */).build();
+                getCellNetworkPriorityBuilder().setRoaming(MATCH_FORBIDDEN).build();
 
         assertFalse(
                 checkMatchesCellPriorityRule(
@@ -353,7 +348,7 @@
                 calculatePriorityClass(
                         mVcnContext,
                         networkRecord,
-                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_PRIORITIES,
+                        VcnGatewayConnectionConfig.DEFAULT_UNDERLYING_NETWORK_TEMPLATES,
                         SUB_GROUP,
                         mSubscriptionSnapshot,
                         null /* currentlySelected */,
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 8975713..fbfbf68 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -19,12 +19,11 @@
 #include "android-base/file.h"
 #include "android-base/stringprintf.h"
 #include "android-base/utf8.h"
-
+#include "format/proto/ProtoDeserialize.h"
 #include "io/StringStream.h"
 #include "io/ZipArchive.h"
 #include "java/AnnotationProcessor.h"
 #include "test/Test.h"
-#include "format/proto/ProtoDeserialize.h"
 
 namespace aapt {
 
@@ -59,55 +58,56 @@
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
                                          "integration-tests", "CompileTest", "res"});
+  const std::string kOutDir = testing::TempDir();
 
   // Resource files without periods in the file name should not throw errors
   const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
-  const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+  const std::string path0_out = BuildPath({kOutDir, "values_values.arsc.flat"});
   ::android::base::utf8::unlink(path0_out.c_str());
-  ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path0, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
 
   const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
-  const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+  const std::string path1_out = BuildPath({kOutDir, "drawable_image.png.flat"});
   ::android::base::utf8::unlink(path1_out.c_str());
-  ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path1, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
 
   const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
-  const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+  const std::string path2_out = BuildPath({kOutDir, "drawable_image.9.png.flat"});
   ::android::base::utf8::unlink(path2_out.c_str());
-  ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path2, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
 
   // Resource files with periods in the file name should fail on non-legacy compilations
   const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
-  const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+  const std::string path3_out = BuildPath({kOutDir, "values_values.all.arsc.flat"});
   ::android::base::utf8::unlink(path3_out.c_str());
-  ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path3, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path3, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
 
   const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
-  const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+  const std::string path4_out = BuildPath({kOutDir, "drawable_image.small.png.flat"});
   ::android::base::utf8::unlink(path4_out.c_str());
-  ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path4, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path4, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
 
   const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
-  const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+  const std::string path5_out = BuildPath({kOutDir, "drawable_image.small.9.png.flat"});
   ::android::base::utf8::unlink(path5_out.c_str());
-  ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
+  ASSERT_NE(TestCompile(path5, kOutDir, /** legacy */ false, diag), 0);
   ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
-  ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
+  ASSERT_EQ(TestCompile(path5, kOutDir, /** legacy */ true, diag), 0);
   ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
 }
 
@@ -116,9 +116,7 @@
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
   const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
                                          "integration-tests", "CompileTest", "DirInput", "res"});
-  const std::string kOutputFlata =
-      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
-                 "CompileTest", "DirInput", "compiled.flata"});
+  const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
   ::android::base::utf8::unlink(kOutputFlata.c_str());
 
   std::vector<android::StringPiece> args;
@@ -147,9 +145,7 @@
   const std::string kResZip =
       BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
                  "CompileTest", "ZipInput", "res.zip"});
-  const std::string kOutputFlata =
-      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
-                 "CompileTest", "ZipInput", "compiled.flata"});
+  const std::string kOutputFlata = BuildPath({testing::TempDir(), "compiled.flata"});
 
   ::android::base::utf8::unlink(kOutputFlata.c_str());
 
@@ -257,9 +253,9 @@
 
 TEST_F(CompilerTest, RelativePathTest) {
   StdErrDiagnostics diag;
-  const std::string res_path = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "res"});
+  const std::string res_path =
+      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+                 "CompileTest", "res"});
 
   const std::string path_values_colors = GetTestPath("values/colors.xml");
   WriteFile(path_values_colors, "<resources>"
@@ -272,9 +268,8 @@
                    "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
                    "</LinearLayout>");
 
-  const std::string compiled_files_dir  = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "compiled"});
+  const std::string compiled_files_dir =
+      BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "compiled"});
   CHECK(file::mkdirs(compiled_files_dir.data()));
 
   const std::string path_values_colors_out =
@@ -283,9 +278,8 @@
       BuildPath({compiled_files_dir, "layout_layout_one.flat"});
   ::android::base::utf8::unlink(path_values_colors_out.c_str());
   ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
-  const std::string apk_path = BuildPath(
-      {android::base::Dirname(android::base::GetExecutablePath()),
-       "integration-tests", "CompileTest", "out.apk"});
+  const std::string apk_path =
+      BuildPath({testing::TempDir(), "integration-tests", "CompileTest", "out.apk"});
 
   const std::string source_set_res = BuildPath({"main", "res"});
   const std::string relative_path_values_colors =
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index e2f71dc..ddc1853 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -67,8 +67,7 @@
 }
 
 void TestDirectoryFixture::SetUp() {
-  temp_dir_ = file::BuildPath({android::base::GetExecutableDirectory(),
-                               "_temp",
+  temp_dir_ = file::BuildPath({testing::TempDir(), "_temp",
                                testing::UnitTest::GetInstance()->current_test_case()->name(),
                                testing::UnitTest::GetInstance()->current_test_info()->name()});
   ASSERT_TRUE(file::mkdirs(temp_dir_));
@@ -236,4 +235,4 @@
   return args_;
 }
 
-} // namespace aapt
\ No newline at end of file
+}  // namespace aapt