Merge "Add getter for the fields of NetworkStats.Entry"
diff --git a/Android.bp b/Android.bp
index 36ed865..310db95 100644
--- a/Android.bp
+++ b/Android.bp
@@ -419,7 +419,6 @@
     name: "framework-ike-shared-srcs",
     visibility: ["//packages/modules/IPsec"],
     srcs: [
-        "core/java/android/net/annotations/PolicyDirection.java",
         "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
         "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 31fecd1..14da6fb 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -177,6 +177,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.module-lib.latest",
             removed_api_file: ":android-non-updatable-removed.api.module-lib.latest",
+            baseline_file: ":android-non-updatable-incompatibilities.api.module-lib.latest",
         },
         api_lint: {
             enabled: true,
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index a1a46af..161a317 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -235,7 +235,6 @@
     public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
     /**
      * All Bluetooth broadcasts.
-     * @hide
      */
     public static final int REASON_BLUETOOTH_BROADCAST = 203;
     /**
diff --git a/api/api.go b/api/api.go
index 976b140..74cbece 100644
--- a/api/api.go
+++ b/api/api.go
@@ -30,22 +30,10 @@
 
 // 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
+	// Module libraries in the bootclasspath
+	Bootclasspath []string
+	// Module libraries in system server
+	System_server_classpath []string
 }
 
 type CombinedApis struct {
@@ -105,7 +93,7 @@
 	props := genruleProps{}
 	props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
 	props.Tools = []string{"metalava"}
-	props.Out = []string{txt.TxtFilename}
+	props.Out = []string{filename}
 	props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --api $(out)")
 	props.Srcs = createSrcs(txt.BaseTxt, txt.Modules, txt.ModuleTag)
 	props.Dists = []android.Dist{
@@ -170,33 +158,38 @@
 
 func createMergedTxts(ctx android.LoadHookContext, props CombinedApisProperties) {
 	var textFiles []MergedTxtDefinition
+	// Two module libraries currently do not support @SystemApi so only have the public scope.
+	bcpWithSystemApi := props.Bootclasspath
+	bcpWithSystemApi = remove(bcpWithSystemApi, "conscrypt.module.public.api")
+	bcpWithSystemApi = remove(bcpWithSystemApi, "i18n.module.public.api")
+
 	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,
+			Modules:     props.Bootclasspath,
 			ModuleTag:   "{.public" + tagSuffix[i],
 			Scope:       "public",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename: f,
 			BaseTxt:     ":non-updatable-system-" + f,
-			Modules:     props.System,
+			Modules:     bcpWithSystemApi,
 			ModuleTag:   "{.system" + tagSuffix[i],
 			Scope:       "system",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename: f,
 			BaseTxt:     ":non-updatable-module-lib-" + f,
-			Modules:     props.Module_lib,
+			Modules:     bcpWithSystemApi,
 			ModuleTag:   "{.module-lib" + tagSuffix[i],
 			Scope:       "module-lib",
 		})
 		textFiles = append(textFiles, MergedTxtDefinition{
 			TxtFilename: f,
 			BaseTxt:     ":non-updatable-system-server-" + f,
-			Modules:     props.System_server,
+			Modules:     props.System_server_classpath,
 			ModuleTag:   "{.system-server" + tagSuffix[i],
 			Scope:       "system-server",
 		})
@@ -209,10 +202,18 @@
 func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
 	createMergedTxts(ctx, a.properties)
 
-	createMergedStubsSrcjar(ctx, a.properties.Public)
+	createMergedStubsSrcjar(ctx, a.properties.Bootclasspath)
 
-	// For the filtered api versions, we prune all APIs except art module's APIs.
-	createFilteredApiVersions(ctx, remove(a.properties.Public, a.properties.Art_module))
+	// For the filtered api versions, we prune all APIs except art module's APIs. 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.
+	filteredModules := a.properties.Bootclasspath
+	filteredModules = remove(filteredModules, "art.module.public.api")
+	createFilteredApiVersions(ctx, filteredModules)
 }
 
 func combinedApisModuleFactory() android.Module {
diff --git a/core/api/current.txt b/core/api/current.txt
index 95435f7..d696bc7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17980,10 +17980,12 @@
     ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher);
     ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac);
-    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
+    ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
     method public javax.crypto.Cipher getCipher();
-    method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
+    method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
     method public javax.crypto.Mac getMac();
+    method @Nullable public android.security.identity.PresentationSession getPresentationSession();
     method public java.security.Signature getSignature();
   }
 
@@ -27290,6 +27292,8 @@
     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 setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnCellUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, 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);
@@ -27350,6 +27354,10 @@
 
   public abstract class VcnUnderlyingNetworkTemplate {
     method public int getMetered();
+    method public int getMinEntryDownstreamBandwidthKbps();
+    method public int getMinEntryUpstreamBandwidthKbps();
+    method public int getMinExitDownstreamBandwidthKbps();
+    method public int getMinExitUpstreamBandwidthKbps();
     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
@@ -27363,6 +27371,8 @@
     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 setMinDownstreamBandwidthKbps(int, int);
+    method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setMinUpstreamBandwidthKbps(int, int);
     method @NonNull public android.net.vcn.VcnWifiUnderlyingNetworkTemplate.Builder setSsids(@NonNull java.util.Set<java.lang.String>);
   }
 
@@ -37542,6 +37552,51 @@
     ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
   }
 
+  public class CredentialDataRequest {
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getDeviceSignedEntriesToRequest();
+    method @NonNull public java.util.Map<java.lang.String,java.util.Collection<java.lang.String>> getIssuerSignedEntriesToRequest();
+    method @Nullable public byte[] getReaderSignature();
+    method @Nullable public byte[] getRequestMessage();
+    method public boolean isAllowUsingExhaustedKeys();
+    method public boolean isAllowUsingExpiredKeys();
+    method public boolean isIncrementUseCount();
+  }
+
+  public static final class CredentialDataRequest.Builder {
+    ctor public CredentialDataRequest.Builder();
+    method @NonNull public android.security.identity.CredentialDataRequest build();
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExhaustedKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setAllowUsingExpiredKeys(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setDeviceSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIncrementUseCount(boolean);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setIssuerSignedEntriesToRequest(@NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setReaderSignature(@NonNull byte[]);
+    method @NonNull public android.security.identity.CredentialDataRequest.Builder setRequestMessage(@NonNull byte[]);
+  }
+
+  public abstract class CredentialDataResult {
+    method @Nullable public abstract byte[] getDeviceMac();
+    method @NonNull public abstract byte[] getDeviceNameSpaces();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getDeviceSignedEntries();
+    method @NonNull public abstract android.security.identity.CredentialDataResult.Entries getIssuerSignedEntries();
+    method @NonNull public abstract byte[] getStaticAuthenticationData();
+  }
+
+  public static interface CredentialDataResult.Entries {
+    method @Nullable public byte[] getEntry(@NonNull String, @NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @NonNull public java.util.Collection<java.lang.String> getNamespaces();
+    method @NonNull public java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method public int getStatus(@NonNull String, @NonNull String);
+    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field public static final int STATUS_OK = 0; // 0x0
+    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  }
+
   public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
     ctor public DocTypeNotSupportedException(@NonNull String);
     ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
@@ -37553,19 +37608,19 @@
   }
 
   public abstract class IdentityCredential {
-    method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
-    method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+    method @Deprecated @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
+    method @Deprecated @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
     method @NonNull public byte[] delete(@NonNull byte[]);
-    method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
+    method @Deprecated @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
     method @NonNull public abstract int[] getAuthenticationDataUsageCount();
     method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
-    method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+    method @Deprecated @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
     method @NonNull public byte[] proveOwnership(@NonNull byte[]);
-    method public abstract void setAllowUsingExhaustedKeys(boolean);
-    method public void setAllowUsingExpiredKeys(boolean);
+    method @Deprecated public abstract void setAllowUsingExhaustedKeys(boolean);
+    method @Deprecated public void setAllowUsingExpiredKeys(boolean);
     method public abstract void setAvailableAuthenticationKeys(int, int);
-    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method @Deprecated public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
     method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
     method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
@@ -37578,6 +37633,7 @@
 
   public abstract class IdentityCredentialStore {
     method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
+    method @NonNull public android.security.identity.PresentationSession createPresentationSession(int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
     method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
     method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
@@ -37616,22 +37672,29 @@
     method @NonNull public android.security.identity.PersonalizationData.Builder putEntry(@NonNull String, @NonNull String, @NonNull java.util.Collection<android.security.identity.AccessControlProfileId>, @NonNull byte[]);
   }
 
-  public abstract class ResultData {
-    method @NonNull public abstract byte[] getAuthenticatedData();
-    method @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
-    method @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
-    method @Nullable public abstract byte[] getMessageAuthenticationCode();
-    method @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
-    method @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
-    method @NonNull public abstract byte[] getStaticAuthenticationData();
-    method public abstract int getStatus(@NonNull String, @NonNull String);
-    field public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
-    field public static final int STATUS_NOT_REQUESTED = 2; // 0x2
-    field public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
-    field public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
-    field public static final int STATUS_OK = 0; // 0x0
-    field public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
-    field public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
+  public abstract class PresentationSession {
+    method @Nullable public abstract android.security.identity.CredentialDataResult getCredentialData(@NonNull String, @NonNull android.security.identity.CredentialDataRequest) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException;
+    method @NonNull public abstract java.security.KeyPair getEphemeralKeyPair();
+    method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+    method public abstract void setSessionTranscript(@NonNull byte[]);
+  }
+
+  @Deprecated public abstract class ResultData {
+    method @Deprecated @NonNull public abstract byte[] getAuthenticatedData();
+    method @Deprecated @Nullable public abstract byte[] getEntry(@NonNull String, @NonNull String);
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getEntryNames(@NonNull String);
+    method @Deprecated @Nullable public abstract byte[] getMessageAuthenticationCode();
+    method @Deprecated @NonNull public abstract java.util.Collection<java.lang.String> getNamespaces();
+    method @Deprecated @Nullable public abstract java.util.Collection<java.lang.String> getRetrievedEntryNames(@NonNull String);
+    method @Deprecated @NonNull public abstract byte[] getStaticAuthenticationData();
+    method @Deprecated public abstract int getStatus(@NonNull String, @NonNull String);
+    field @Deprecated public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3; // 0x3
+    field @Deprecated public static final int STATUS_NOT_REQUESTED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6; // 0x6
+    field @Deprecated public static final int STATUS_NO_SUCH_ENTRY = 1; // 0x1
+    field @Deprecated public static final int STATUS_OK = 0; // 0x0
+    field @Deprecated public static final int STATUS_READER_AUTHENTICATION_FAILED = 5; // 0x5
+    field @Deprecated public static final int STATUS_USER_AUTHENTICATION_FAILED = 4; // 0x4
   }
 
   public class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
@@ -41254,6 +41317,7 @@
     field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
     field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
     field public static final int EPDG_ADDRESS_STATIC = 0; // 0x0
+    field public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4; // 0x4
     field public static final int ID_TYPE_FQDN = 2; // 0x2
     field public static final int ID_TYPE_KEY_ID = 11; // 0xb
     field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d13170bf..b380e6d 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -71,6 +71,14 @@
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
+  public class Intent implements java.lang.Cloneable android.os.Parcelable {
+    field public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
+    field public static final String EXTRA_SETTING_NAME = "setting_name";
+    field public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
+    field public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
+    field public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
+  }
+
 }
 
 package android.content.pm {
@@ -127,6 +135,7 @@
 
   public final class BtProfileConnectionInfo implements android.os.Parcelable {
     method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
+    method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
     method public int describeContents();
     method public boolean getIsLeOutput();
     method public int getProfile();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 8ace816..8556011 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -138,6 +138,7 @@
     field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS";
     field public static final String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
     field public static final String MANAGE_AUTO_FILL = "android.permission.MANAGE_AUTO_FILL";
+    field public static final String MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED = "android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED";
     field public static final String MANAGE_CARRIER_OEM_UNLOCK_STATE = "android.permission.MANAGE_CARRIER_OEM_UNLOCK_STATE";
     field public static final String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
@@ -5880,6 +5881,7 @@
 
   public static final class TvInputManager.Hardware {
     method public void overrideAudioSink(int, String, int, int, int);
+    method public void overrideAudioSink(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int, int, int);
     method public void setStreamVolume(float);
     method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
   }
@@ -8517,6 +8519,7 @@
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
     field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
     field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field public static final int REASON_BLUETOOTH_BROADCAST = 203; // 0xcb
     field public static final int REASON_GEOFENCING = 100; // 0x64
     field public static final int REASON_LOCATION_PROVIDER = 312; // 0x138
     field public static final int REASON_OTHER = 1; // 0x1
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index c6c64b0..f94ee85 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -28,8 +28,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
+import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache;
 import android.bluetooth.BluetoothDevice.Transport;
 import android.bluetooth.BluetoothProfile.ConnectionPolicy;
 import android.bluetooth.annotations.RequiresBluetoothAdvertisePermission;
@@ -676,14 +675,15 @@
             "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
 
     /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
+    public static final int STATE_DISCONNECTED =
+            0; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
     /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
+    public static final int STATE_CONNECTING = 1; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTING;
     /** The profile is in connected state */
-    public static final int STATE_CONNECTED = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
+    public static final int STATE_CONNECTED = 2; //BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
     /** The profile is in disconnecting state */
     public static final int STATE_DISCONNECTING =
-            BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
+            3; //BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTING;
 
     /** @hide */
     public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
@@ -1044,6 +1044,7 @@
         return false;
     }
 
+    /*
     private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
 
     private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
@@ -1059,17 +1060,22 @@
                     }
                 }
             };
+     */
 
     /** @hide */
+    /*
     @RequiresNoPermission
     public void disableBluetoothGetStateCache() {
         mBluetoothGetStateCache.disableLocal();
     }
+     */
 
     /** @hide */
+    /*
     public static void invalidateBluetoothGetStateCache() {
         PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
     }
+     */
 
     /**
      * Fetch the current bluetooth state.  If the service is down, return
@@ -1081,14 +1087,12 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                state = mBluetoothGetStateCache.query(null);
+                //state = mBluetoothGetStateCache.query(null);
+                state = mService.getState();
             }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e.getCause());
-            } else {
-                throw e;
-            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            e.rethrowFromSystemServer();
         } finally {
             mServiceLock.readLock().unlock();
         }
@@ -2100,6 +2104,7 @@
         }
     }
 
+    /*
     private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
             "cache_key.bluetooth.is_offloaded_filtering_supported";
     private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
@@ -2122,17 +2127,22 @@
 
                 }
             };
+     */
 
     /** @hide */
+    /*
     @RequiresNoPermission
     public void disableIsOffloadedFilteringSupportedCache() {
         mBluetoothFilteringCache.disableLocal();
     }
+     */
 
     /** @hide */
+    /*
     public static void invalidateIsOffloadedFilteringSupportedCache() {
         PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
     }
+     */
 
     /**
      * Return true if offloaded filters are supported
@@ -2145,7 +2155,18 @@
         if (!getLeAccess()) {
             return false;
         }
-        return mBluetoothFilteringCache.query(null);
+        //return mBluetoothFilteringCache.query(null);
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.isOffloadedFilteringSupported();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
     }
 
     /**
@@ -2551,15 +2572,13 @@
         return supportedProfiles;
     }
 
+    /*
     private static final String BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY =
             "cache_key.bluetooth.get_adapter_connection_state";
     private final PropertyInvalidatedCache<Void, Integer>
             mBluetoothGetAdapterConnectionStateCache =
             new PropertyInvalidatedCache<Void, Integer> (
                 8, BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY) {
-                /**
-                 * This method must not be called when mService is null.
-                 */
                 @Override
                 @SuppressLint("AndroidFrameworkRequiresPermission")
                 protected Integer recompute(Void query) {
@@ -2570,18 +2589,23 @@
                     }
                 }
             };
+     */
 
     /** @hide */
+    /*
     @RequiresNoPermission
     public void disableGetAdapterConnectionStateCache() {
         mBluetoothGetAdapterConnectionStateCache.disableLocal();
     }
+     */
 
     /** @hide */
+    /*
     public static void invalidateGetAdapterConnectionStateCache() {
         PropertyInvalidatedCache.invalidateCache(
             BLUETOOTH_GET_ADAPTER_CONNECTION_STATE_CACHE_PROPERTY);
     }
+     */
 
     /**
      * Get the current connection state of the local Bluetooth adapter.
@@ -2605,20 +2629,18 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                return mBluetoothGetAdapterConnectionStateCache.query(null);
+                return mService.getAdapterConnectionState();
             }
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "getConnectionState:", e.getCause());
-            } else {
-                throw e;
-            }
+            //return mBluetoothGetAdapterConnectionStateCache.query(null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to getConnectionState, error: ", e);
         } finally {
             mServiceLock.readLock().unlock();
         }
         return BluetoothAdapter.STATE_DISCONNECTED;
     }
 
+    /*
     private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
             "cache_key.bluetooth.get_profile_connection_state";
     private final PropertyInvalidatedCache<Integer, Integer>
@@ -2646,17 +2668,22 @@
                                          query);
                 }
             };
+     */
 
     /** @hide */
+    /*
     @RequiresNoPermission
     public void disableGetProfileConnectionStateCache() {
         mGetProfileConnectionStateCache.disableLocal();
     }
+     */
 
     /** @hide */
+    /*
     public static void invalidateGetProfileConnectionStateCache() {
         PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
     }
+     */
 
     /**
      * Get the current connection state of a profile.
@@ -2678,7 +2705,18 @@
         if (getState() != STATE_ON) {
             return BluetoothProfile.STATE_DISCONNECTED;
         }
-        return mGetProfileConnectionStateCache.query(new Integer(profile));
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                mService.getProfileConnectionState(profile);
+            }
+            //return mGetProfileConnectionStateCache.query(new Integer(profile));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to getProfileConnectionState, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return BluetoothProfile.STATE_DISCONNECTED;
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 93f0268..1edf5cc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -23,8 +23,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SuppressLint;
-import android.annotation.SystemApi;
-import android.app.PropertyInvalidatedCache;
+import android.annotation.SystemApi; //import android.app.PropertyInvalidatedCache;
 import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
 import android.bluetooth.annotations.RequiresBluetoothLocationPermission;
 import android.bluetooth.annotations.RequiresBluetoothScanPermission;
@@ -1597,6 +1596,7 @@
         return false;
     }
 
+    /*
     private static final String BLUETOOTH_BONDING_CACHE_PROPERTY =
             "cache_key.bluetooth.get_bond_state";
     private final PropertyInvalidatedCache<BluetoothDevice, Integer> mBluetoothBondCache =
@@ -1612,16 +1612,19 @@
                     }
                 }
             };
+     */
 
     /** @hide */
-    public void disableBluetoothGetBondStateCache() {
+    /* public void disableBluetoothGetBondStateCache() {
         mBluetoothBondCache.disableLocal();
-    }
+    } */
 
     /** @hide */
+    /*
     public static void invalidateBluetoothGetBondStateCache() {
         PropertyInvalidatedCache.invalidateCache(BLUETOOTH_BONDING_CACHE_PROPERTY);
     }
+     */
 
     /**
      * Get the bond state of the remote device.
@@ -1643,13 +1646,11 @@
             return BOND_NONE;
         }
         try {
-            return mBluetoothBondCache.query(this);
-        } catch (RuntimeException e) {
-            if (e.getCause() instanceof RemoteException) {
-                Log.e(TAG, "", e);
-            } else {
-                throw e;
-            }
+            //return mBluetoothBondCache.query(this);
+            return sService.getBondState(this, mAttributionSource);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to ", e);
+            e.rethrowFromSystemServer();
         }
         return BOND_NONE;
     }
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index fbee577..8e9d7b7 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -1,6 +1,4 @@
 # Bug component: 27441
 
-rahulsabnis@google.com
 sattiraju@google.com
-siyuanh@google.com
-zachoverflow@google.com
+baligh@google.com
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9659df6..8a5e097 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4206,9 +4206,9 @@
      * restored from (corresponds to {@link android.os.Build.VERSION#SDK_INT}). The first three
      * values are represented as strings, the fourth one as int.
      *
-     * <p>This broadcast is sent only for settings provider entries known to require special handling
-     * around restore time.  These entries are found in the BROADCAST_ON_RESTORE table within
-     * the provider's backup agent implementation.
+     * <p>This broadcast is sent only for settings provider entries known to require special
+     * handling around restore time to specific receivers. These entries are found in the
+     * BROADCAST_ON_RESTORE table within the provider's backup agent implementation.
      *
      * @see #EXTRA_SETTING_NAME
      * @see #EXTRA_SETTING_PREVIOUS_VALUE
@@ -4216,15 +4216,46 @@
      * @see #EXTRA_SETTING_RESTORED_FROM_SDK_INT
      * {@hide}
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String ACTION_SETTING_RESTORED = "android.os.action.SETTING_RESTORED";
 
-    /** {@hide} */
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the name of the restored setting.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_NAME = "setting_name";
-    /** {@hide} */
+
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry prior to the restore
+     * operation.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_PREVIOUS_VALUE = "previous_value";
-    /** {@hide} */
+
+    /**
+     * String intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the value of the {@link EXTRA_SETTING_NAME} settings entry being restored.
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_NEW_VALUE = "new_value";
-    /** {@hide} */
+
+    /**
+     * Int intent extra to be used with {@link ACTION_SETTING_RESTORED}.
+     * Contain the version of the SDK that the setting has been restored from (corresponds to
+     * {@link android.os.Build.VERSION#SDK_INT}).
+     * {@hide}
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("ActionValue")
     public static final String EXTRA_SETTING_RESTORED_FROM_SDK_INT = "restored_from_sdk_int";
 
     /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2ed00b5..7cd7e7ac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2485,6 +2485,8 @@
      * API shipped in Android 11.
      * <li><code>202101</code>: corresponds to the features included in the Identity Credential
      * API shipped in Android 12.
+     * <li><code>202201</code>: corresponds to the features included in the Identity Credential
+     * API shipped in Android 13.
      * </ul>
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index c8c122d..9fb70d6 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore.KeyProperties;
 import android.text.TextUtils;
 import android.util.Log;
@@ -653,8 +654,8 @@
     /**
      * A wrapper class for the cryptographic operations supported by BiometricPrompt.
      *
-     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac}, and
-     * {@link IdentityCredential}.
+     * <p>Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+     * {@link IdentityCredential}, and {@link PresentationSession}.
      *
      * <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
      * time-based. This is specified during key creation via the timeout parameter of the
@@ -684,10 +685,21 @@
             super(mac);
         }
 
+        /**
+         * Create from a {@link IdentityCredential} object.
+         *
+         * @param credential a {@link IdentityCredential} object.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+         */
+        @Deprecated
         public CryptoObject(@NonNull IdentityCredential credential) {
             super(credential);
         }
 
+        public CryptoObject(@NonNull PresentationSession session) {
+            super(session);
+        }
+
         /**
          * Get {@link Signature} object.
          * @return {@link Signature} object or null if this doesn't contain one.
@@ -715,10 +727,20 @@
         /**
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public @Nullable IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         */
+        public @Nullable PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 7648cf2..d415706 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.security.keystore2.AndroidKeyStoreProvider;
 
 import java.security.Signature;
@@ -27,8 +28,8 @@
 
 /**
  * A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
- * {@link IdentityCredential} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac},
+ * {@link IdentityCredential}, and {@link PresentationSession} objects.
  * @hide
  */
 public class CryptoObject {
@@ -46,10 +47,21 @@
         mCrypto = mac;
     }
 
+    /**
+     * Create from a {@link IdentityCredential} object.
+     *
+     * @param credential a {@link IdentityCredential} object.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
+     */
+    @Deprecated
     public CryptoObject(@NonNull IdentityCredential credential) {
         mCrypto = credential;
     }
 
+    public CryptoObject(@NonNull PresentationSession session) {
+        mCrypto = session;
+    }
+
     /**
      * Get {@link Signature} object.
      * @return {@link Signature} object or null if this doesn't contain one.
@@ -77,12 +89,22 @@
     /**
      * Get {@link IdentityCredential} object.
      * @return {@link IdentityCredential} object or null if this doesn't contain one.
+     * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
      */
+    @Deprecated
     public IdentityCredential getIdentityCredential() {
         return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
     }
 
     /**
+     * Get {@link PresentationSession} object.
+     * @return {@link PresentationSession} object or null if this doesn't contain one.
+     */
+    public PresentationSession getPresentationSession() {
+        return mCrypto instanceof PresentationSession ? (PresentationSession) mCrypto : null;
+    }
+
+    /**
      * @hide
      * @return the opId associated with this object or 0 if none
      */
@@ -91,6 +113,8 @@
             return 0;
         } else if (mCrypto instanceof IdentityCredential) {
             return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
+        } else if (mCrypto instanceof PresentationSession) {
+            return ((PresentationSession) mCrypto).getCredstoreOperationHandle();
         }
         return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto);
     }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a3d595c..480923e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -58,6 +58,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
 import android.util.Slog;
 import android.view.Surface;
 
@@ -264,10 +265,21 @@
          * Get {@link IdentityCredential} object.
          * @return {@link IdentityCredential} object or null if this doesn't contain one.
          * @hide
+         * @deprecated Use {@link PresentationSession} instead of {@link IdentityCredential}.
          */
+        @Deprecated
         public IdentityCredential getIdentityCredential() {
             return super.getIdentityCredential();
         }
+
+        /**
+         * Get {@link PresentationSession} object.
+         * @return {@link PresentationSession} object or null if this doesn't contain one.
+         * @hide
+         */
+        public PresentationSession getPresentationSession() {
+            return super.getPresentationSession();
+        }
     }
 
     /**
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f50aa99..147138e 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -69,6 +69,8 @@
 
     int getMultipathPreference(in Network network);
 
+    SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
+    void onStatsProviderWarningOrLimitReached();
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
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/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 7ebb646..426fc61 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -535,6 +535,46 @@
     }
 
     /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     * @return the active {@link SubscriptionPlan} for the given template, or
+     *         {@code null} if not found.
+     * @hide
+     */
+    @Nullable
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        try {
+            return mService.getSubscriptionPlan(template);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    // @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void onStatsProviderWarningOrLimitReached() {
+        try {
+            mService.onStatsProviderWarningOrLimitReached();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Resets network policy settings back to factory defaults.
      *
      * @hide
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 5c28553..3193826 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -96,6 +96,141 @@
      */
     public static final String NOTIFICATION_CHANNEL_VPN = "VPN";
 
+    /**
+     * Action sent in the intent when an error occurred.
+     *
+     * @hide
+     */
+    public static final String ACTION_VPN_MANAGER_ERROR = "android.net.action.VPN_MANAGER_ERROR";
+
+    /**
+     * An IKE protocol error. Codes are the codes from IkeProtocolException, RFC 7296.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_IKE = "android.net.category.ERROR_IKE";
+
+    /**
+     * User deactivated the VPN, either by turning it off or selecting a different VPN provider.
+     * The error code is always 0.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_USER_DEACTIVATED =
+            "android.net.category.ERROR_USER_DEACTIVATED";
+
+    /**
+     * Network error. Error codes are ERROR_CODE_NETWORK_*.
+     *
+     * @hide
+     */
+    public static final String CATEGORY_ERROR_NETWORK = "android.net.category.ERROR_NETWORK";
+
+    /**
+     * The key of the session that experienced this error, as returned by
+     * startProvisionedVpnProfileSession.
+     *
+     * @hide
+     */
+    public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
+
+    /**
+     * Extra for the Network object that was the underlying network at the time of the failure, or
+     * null if none.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
+
+    /**
+     * The NetworkCapabilities of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
+            "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
+
+    /**
+     * The LinkProperties of the underlying network.
+     *
+     * @hide
+     */
+    public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
+            "android.net.extra.UNDERLYING_LINK_PROPERTIES";
+
+    /**
+     * A long timestamp with SystemClock.elapsedRealtime base for when the event happened.
+     *
+     * @hide
+     */
+    public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+
+    /**
+     * Extra for the error type. This is ERROR_NOT_RECOVERABLE or ERROR_RECOVERABLE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_TYPE = "android.net.extra.ERROR_TYPE";
+
+    /**
+     * Extra for the error code. The value will be 0 for CATEGORY_ERROR_USER_DEACTIVATED, one of
+     * ERROR_CODE_NETWORK_* for ERROR_CATEGORY_NETWORK or one of values defined in
+     * IkeProtocolException#ErrorType for CATEGORY_ERROR_IKE.
+     *
+     * @hide
+     */
+    public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
+
+    /**
+     * This error is fatal, e.g. the VPN was disabled or configuration error. The stack will not
+     * retry connection.
+     *
+     * @hide
+     */
+    public static final int ERROR_NOT_RECOVERABLE = 1;
+
+    /**
+     * The stack experienced an error but will retry with exponential backoff, e.g. network timeout.
+     *
+     * @hide
+     */
+    public static final int ERROR_RECOVERABLE = 2;
+
+    /**
+     * An error code to indicate that there was an UnknownHostException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_UNKNOWN_HOST = 0;
+
+    /**
+     * An error code to indicate that there is a SocketTimeoutException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_TIMEOUT = 1;
+
+    /**
+     * An error code to indicate that the connection is refused.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECT = 2;
+
+    /**
+     * An error code to indicate the connection was reset. (e.g. SocketException)
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_CONNECTION_RESET = 3;
+
+    /**
+     * An error code to indicate that there is an IOException.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_NETWORK_IO = 4;
+
     /** @hide */
     @IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
             TYPE_VPN_OEM})
diff --git a/core/java/android/net/annotations/PolicyDirection.java b/core/java/android/net/annotations/PolicyDirection.java
index febd9b4..3f7521a 100644
--- a/core/java/android/net/annotations/PolicyDirection.java
+++ b/core/java/android/net/annotations/PolicyDirection.java
@@ -24,10 +24,6 @@
 
 /**
  * IPsec traffic direction.
- *
- * <p>Mainline modules cannot reference hidden @IntDef. Moving this annotation to a separate class
- * to allow others to statically include it.
- *
  * @hide
  */
 @IntDef(value = {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT})
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 125b573..69e6313 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -63,13 +63,22 @@
     private final int mOpportunisticMatchCriteria;
 
     private VcnCellUnderlyingNetworkTemplate(
-            int networkQuality,
             int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
             Set<String> allowedNetworkPlmnIds,
             Set<Integer> allowedSpecificCarrierIds,
             int roamingMatchCriteria,
             int opportunisticMatchCriteria) {
-        super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, meteredMatchCriteria);
+        super(
+                NETWORK_PRIORITY_TYPE_CELL,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
         mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
         mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
         mRoamingMatchCriteria = roamingMatchCriteria;
@@ -109,9 +118,17 @@
             @NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle is null");
 
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
         final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
 
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
         final PersistableBundle plmnIdsBundle =
                 in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
         Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
@@ -131,8 +148,11 @@
         final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY);
 
         return new VcnCellUnderlyingNetworkTemplate(
-                networkQuality,
                 meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
                 allowedNetworkPlmnIds,
                 allowedSpecificCarrierIds,
                 roamingMatchCriteria,
@@ -243,7 +263,6 @@
 
     /** 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<>();
@@ -252,29 +271,15 @@
         private int mRoamingMatchCriteria = MATCH_ANY;
         private int mOpportunisticMatchCriteria = MATCH_ANY;
 
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * 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
@@ -369,12 +374,92 @@
             return this;
         }
 
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
         /** Build the VcnCellUnderlyingNetworkTemplate. */
         @NonNull
         public VcnCellUnderlyingNetworkTemplate build() {
             return new VcnCellUnderlyingNetworkTemplate(
-                    mNetworkQuality,
                     mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
                     mAllowedNetworkPlmnIds,
                     mAllowedSpecificCarrierIds,
                     mRoamingMatchCriteria,
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 92956e8..a6830b7 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,7 +17,6 @@
 
 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;
 
@@ -169,18 +168,15 @@
     static {
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .setOpportunistic(MATCH_REQUIRED)
                         .build());
 
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .build());
 
         DEFAULT_UNDERLYING_NETWORK_TEMPLATES.add(
                 new VcnCellUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .build());
     }
 
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 60fc936..3a9ca3e 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -48,23 +48,6 @@
     /** @hide */
     static final int NETWORK_PRIORITY_TYPE_CELL = 2;
 
-    /** Denotes that any network quality is acceptable. @hide */
-    public static final int NETWORK_QUALITY_ANY = 0;
-    /** 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<>();
-
-    static {
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_ANY, "NETWORK_QUALITY_ANY");
-        NETWORK_QUALITY_TO_STRING_MAP.put(NETWORK_QUALITY_OK, "NETWORK_QUALITY_OK");
-    }
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @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
@@ -103,44 +86,73 @@
     private final int mNetworkPriorityType;
 
     /** @hide */
-    static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
-
-    private final int mNetworkQuality;
-
-    /** @hide */
     static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
 
     private final int mMeteredMatchCriteria;
 
     /** @hide */
+    public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0;
+
+    /** @hide */
+    static final String MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinEntryUpstreamBandwidthKbps";
+
+    private final int mMinEntryUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitUpstreamBandwidthKbps";
+
+    private final int mMinExitUpstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY =
+            "mMinEntryDownstreamBandwidthKbps";
+
+    private final int mMinEntryDownstreamBandwidthKbps;
+
+    /** @hide */
+    static final String MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY = "mMinExitDownstreamBandwidthKbps";
+
+    private final int mMinExitDownstreamBandwidthKbps;
+
+    /** @hide */
     VcnUnderlyingNetworkTemplate(
-            int networkPriorityType, int networkQuality, int meteredMatchCriteria) {
+            int networkPriorityType,
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps) {
         mNetworkPriorityType = networkPriorityType;
-        mNetworkQuality = networkQuality;
         mMeteredMatchCriteria = meteredMatchCriteria;
+        mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+        mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+        mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+        mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
     }
 
     /** @hide */
-    static void validateNetworkQuality(int networkQuality) {
+    static void validateMatchCriteria(int matchCriteria, String matchingCapability) {
         Preconditions.checkArgument(
-                networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
-                "Invalid networkQuality:" + networkQuality);
+                MATCH_CRITERIA_TO_STRING_MAP.contains(matchCriteria),
+                "Invalid matching criteria: " + matchCriteria + " for " + matchingCapability);
     }
 
     /** @hide */
-    static void validateMatchCriteria(int meteredMatchCriteria, String matchingCapability) {
+    static void validateMinBandwidthKbps(int minEntryBandwidth, int minExitBandwidth) {
         Preconditions.checkArgument(
-                MATCH_CRITERIA_TO_STRING_MAP.contains(meteredMatchCriteria),
-                "Invalid matching criteria: "
-                        + meteredMatchCriteria
-                        + " for "
-                        + matchingCapability);
+                minEntryBandwidth >= 0, "Invalid minEntryBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minExitBandwidth >= 0, "Invalid minExitBandwidth, must be >= 0");
+        Preconditions.checkArgument(
+                minEntryBandwidth >= minExitBandwidth,
+                "Minimum entry bandwidth must be >= exit bandwidth");
     }
 
     /** @hide */
     protected void validate() {
-        validateNetworkQuality(mNetworkQuality);
         validateMatchCriteria(mMeteredMatchCriteria, "mMeteredMatchCriteria");
+        validateMinBandwidthKbps(mMinEntryUpstreamBandwidthKbps, mMinExitUpstreamBandwidthKbps);
+        validateMinBandwidthKbps(mMinEntryDownstreamBandwidthKbps, mMinExitDownstreamBandwidthKbps);
     }
 
     /** @hide */
@@ -168,15 +180,24 @@
         final PersistableBundle result = new PersistableBundle();
 
         result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
-        result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
         result.putInt(METERED_MATCH_KEY, mMeteredMatchCriteria);
+        result.putInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryUpstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, mMinExitUpstreamBandwidthKbps);
+        result.putInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinEntryDownstreamBandwidthKbps);
+        result.putInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, mMinExitDownstreamBandwidthKbps);
 
         return result;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mNetworkPriorityType, mNetworkQuality, mMeteredMatchCriteria);
+        return Objects.hash(
+                mNetworkPriorityType,
+                mMeteredMatchCriteria,
+                mMinEntryUpstreamBandwidthKbps,
+                mMinExitUpstreamBandwidthKbps,
+                mMinEntryDownstreamBandwidthKbps,
+                mMinExitDownstreamBandwidthKbps);
     }
 
     @Override
@@ -187,8 +208,11 @@
 
         final VcnUnderlyingNetworkTemplate rhs = (VcnUnderlyingNetworkTemplate) other;
         return mNetworkPriorityType == rhs.mNetworkPriorityType
-                && mNetworkQuality == rhs.mNetworkQuality
-                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria;
+                && mMeteredMatchCriteria == rhs.mMeteredMatchCriteria
+                && mMinEntryUpstreamBandwidthKbps == rhs.mMinEntryUpstreamBandwidthKbps
+                && mMinExitUpstreamBandwidthKbps == rhs.mMinExitUpstreamBandwidthKbps
+                && mMinEntryDownstreamBandwidthKbps == rhs.mMinEntryDownstreamBandwidthKbps
+                && mMinExitDownstreamBandwidthKbps == rhs.mMinExitDownstreamBandwidthKbps;
     }
 
     /** @hide */
@@ -197,8 +221,8 @@
     }
 
     /** @hide */
-    static String getMatchCriteriaString(int meteredMatchCriteria) {
-        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, meteredMatchCriteria);
+    static String getMatchCriteriaString(int matchCriteria) {
+        return getNameString(MATCH_CRITERIA_TO_STRING_MAP, matchCriteria);
     }
 
     /** @hide */
@@ -213,34 +237,63 @@
         pw.println(this.getClass().getSimpleName() + ":");
         pw.increaseIndent();
 
-        pw.println(
-                "mNetworkQuality: "
-                        + getNameString(NETWORK_QUALITY_TO_STRING_MAP, mNetworkQuality));
         pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+        pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+        pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+        pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+        pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
         dumpTransportSpecificFields(pw);
 
         pw.decreaseIndent();
     }
 
     /**
-     * Retrieve the required network quality to match this template.
-     *
-     * @see Builder#setNetworkQuality(int)
-     * @hide
-     */
-    @NetworkQuality
-    public int getNetworkQuality() {
-        return mNetworkQuality;
-    }
-
-    /**
      * Return the matching criteria for metered networks.
      *
      * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMetered(int)
      * @see VcnCellUnderlyingNetworkTemplate.Builder#setMetered(int)
      */
-    @MatchCriteria
     public int getMetered() {
         return mMeteredMatchCriteria;
     }
+
+    /**
+     * Returns the minimum entry upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryUpstreamBandwidthKbps() {
+        return mMinEntryUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit upstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinUpstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitUpstreamBandwidthKbps() {
+        return mMinExitUpstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum entry downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinEntryDownstreamBandwidthKbps() {
+        return mMinEntryDownstreamBandwidthKbps;
+    }
+
+    /**
+     * Returns the minimum exit downstream bandwidth allowed by this template.
+     *
+     * @see VcnWifiUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     * @see VcnCellUnderlyingNetworkTemplate.Builder#setMinDownstreamBandwidthKbps(int, int)
+     */
+    public int getMinExitDownstreamBandwidthKbps() {
+        return mMinExitDownstreamBandwidthKbps;
+    }
 }
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 272ca9d..23a07ab 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -46,8 +46,19 @@
     @Nullable private final Set<String> mSsids;
 
     private VcnWifiUnderlyingNetworkTemplate(
-            int networkQuality, int meteredMatchCriteria, Set<String> ssids) {
-        super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, meteredMatchCriteria);
+            int meteredMatchCriteria,
+            int minEntryUpstreamBandwidthKbps,
+            int minExitUpstreamBandwidthKbps,
+            int minEntryDownstreamBandwidthKbps,
+            int minExitDownstreamBandwidthKbps,
+            Set<String> ssids) {
+        super(
+                NETWORK_PRIORITY_TYPE_WIFI,
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps);
         mSsids = new ArraySet<>(ssids);
 
         validate();
@@ -75,15 +86,29 @@
             @NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle is null");
 
-        final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
         final int meteredMatchCriteria = in.getInt(METERED_MATCH_KEY);
 
+        final int minEntryUpstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitUpstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minEntryDownstreamBandwidthKbps =
+                in.getInt(MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+        final int minExitDownstreamBandwidthKbps =
+                in.getInt(MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS_KEY, DEFAULT_MIN_BANDWIDTH_KBPS);
+
         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);
+        return new VcnWifiUnderlyingNetworkTemplate(
+                meteredMatchCriteria,
+                minEntryUpstreamBandwidthKbps,
+                minExitUpstreamBandwidthKbps,
+                minEntryDownstreamBandwidthKbps,
+                minExitDownstreamBandwidthKbps,
+                ssids);
     }
 
     /** @hide */
@@ -137,33 +162,18 @@
 
     /** This class is used to incrementally build VcnWifiUnderlyingNetworkTemplate objects. */
     public static final class Builder {
-        private int mNetworkQuality = NETWORK_QUALITY_ANY;
         private int mMeteredMatchCriteria = MATCH_ANY;
         @NonNull private final Set<String> mSsids = new ArraySet<>();
 
+        private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinEntryDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+        private int mMinExitDownstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
+
         /** Construct a Builder object. */
         public Builder() {}
 
         /**
-         * 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
@@ -200,11 +210,93 @@
             return this;
         }
 
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryUpstreamBandwidthKbps the minimum accepted upstream bandwidth for networks
+         *     that ARE NOT the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @param minExitUpstreamBandwidthKbps the minimum accepted upstream bandwidth for a network
+         *     that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryUpstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitUpstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinUpstreamBandwidthKbps(
+                int minEntryUpstreamBandwidthKbps, int minExitUpstreamBandwidthKbps) {
+            validateMinBandwidthKbps(minEntryUpstreamBandwidthKbps, minExitUpstreamBandwidthKbps);
+
+            mMinEntryUpstreamBandwidthKbps = minEntryUpstreamBandwidthKbps;
+            mMinExitUpstreamBandwidthKbps = minExitUpstreamBandwidthKbps;
+
+            return this;
+        }
+
+        /**
+         * Set the minimum upstream bandwidths that this template will match.
+         *
+         * <p>This template will not match a network that does not provide at least the bandwidth
+         * passed as the entry bandwidth, except in the case that the network is selected as the VCN
+         * Gateway Connection's underlying network, where it will continue to match until the
+         * bandwidth drops under the exit bandwidth.
+         *
+         * <p>The entry criteria MUST be greater than, or equal to the exit criteria to avoid the
+         * invalid case where a network fulfills the entry criteria, but at the same time fails the
+         * exit criteria.
+         *
+         * <p>Estimated bandwidth of a network is provided by the transport layer, and reported in
+         * {@link NetworkCapabilities}. The provided estimates will be used without modification.
+         *
+         * @param minEntryDownstreamBandwidthKbps the minimum accepted downstream bandwidth for
+         *     networks that ARE NOT the already-selected underlying network, or {@code 0} to
+         *     disable this requirement. Disabled by default.
+         * @param minExitDownstreamBandwidthKbps the minimum accepted downstream bandwidth for a
+         *     network that IS the already-selected underlying network, or {@code 0} to disable this
+         *     requirement. Disabled by default.
+         * @return this {@link Builder} instance, for chaining
+         */
+        @NonNull
+        // The getter for the two integers are separated, and in the superclass. Please see {@link
+        // VcnUnderlyingNetworkTemplate#getMinEntryDownstreamBandwidthKbps()} and {@link
+        // VcnUnderlyingNetworkTemplate#getMinExitDownstreamBandwidthKbps()}
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public Builder setMinDownstreamBandwidthKbps(
+                int minEntryDownstreamBandwidthKbps, int minExitDownstreamBandwidthKbps) {
+            validateMinBandwidthKbps(
+                    minEntryDownstreamBandwidthKbps, minExitDownstreamBandwidthKbps);
+
+            mMinEntryDownstreamBandwidthKbps = minEntryDownstreamBandwidthKbps;
+            mMinExitDownstreamBandwidthKbps = minExitDownstreamBandwidthKbps;
+
+            return this;
+        }
+
         /** Build the VcnWifiUnderlyingNetworkTemplate. */
         @NonNull
         public VcnWifiUnderlyingNetworkTemplate build() {
             return new VcnWifiUnderlyingNetworkTemplate(
-                    mNetworkQuality, mMeteredMatchCriteria, mSsids);
+                    mMeteredMatchCriteria,
+                    mMinEntryUpstreamBandwidthKbps,
+                    mMinExitUpstreamBandwidthKbps,
+                    mMinEntryDownstreamBandwidthKbps,
+                    mMinExitDownstreamBandwidthKbps,
+                    mSsids);
         }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9eaaa91..542de3f 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -131,6 +131,7 @@
                     mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -152,6 +153,7 @@
             mSubscriptionChangedListenerMap.remove(listener);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -194,6 +196,7 @@
                     mContext.getAttributionTag(), callback);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -216,6 +219,7 @@
             mOpportunisticSubscriptionChangedListenerMap.remove(listener);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -304,6 +308,7 @@
             sRegistry.notifyCarrierNetworkChange(active);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -329,6 +334,7 @@
             sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -347,6 +353,7 @@
             sRegistry.notifyCallState(slotIndex, subId, state, incomingNumber);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -364,6 +371,7 @@
             sRegistry.notifyCallStateForAllSubs(state, incomingNumber);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -376,6 +384,7 @@
             sRegistry.notifySubscriptionInfoChanged();
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -388,6 +397,7 @@
             sRegistry.notifyOpportunisticSubscriptionInfoChanged();
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -404,6 +414,7 @@
             sRegistry.notifyServiceStateForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -421,6 +432,7 @@
             sRegistry.notifySignalStrengthForPhoneId(slotIndex, subId, signalStrength);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -439,6 +451,7 @@
             sRegistry.notifyMessageWaitingChangedForPhoneId(slotIndex, subId, msgWaitingInd);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -454,6 +467,7 @@
             sRegistry.notifyCallForwardingChangedForSubscriber(subId, callForwardInd);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -469,6 +483,7 @@
             sRegistry.notifyDataActivityForSubscriber(subId, dataActivityType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -490,6 +505,7 @@
                     slotIndex, subId, preciseState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -508,6 +524,7 @@
             sRegistry.notifyCallQualityChanged(callQuality, slotIndex, subId, networkType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -523,6 +540,7 @@
             sRegistry.notifyEmergencyNumberList(slotIndex, subId);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -538,6 +556,7 @@
             sRegistry.notifyOutgoingEmergencyCall(phoneId, subId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -553,6 +572,7 @@
             sRegistry.notifyOutgoingEmergencySms(phoneId, subId, emergencyNumber);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -570,6 +590,7 @@
             sRegistry.notifyRadioPowerStateChanged(slotIndex, subId, radioPowerState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -583,6 +604,7 @@
             sRegistry.notifyPhoneCapabilityChanged(phoneCapability);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -615,6 +637,7 @@
                     SIM_ACTIVATION_TYPE_DATA, activationState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -634,6 +657,7 @@
                     SIM_ACTIVATION_TYPE_VOICE, activationState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -651,6 +675,7 @@
             sRegistry.notifyUserMobileDataStateChangedForPhoneId(slotIndex, subId, state);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -669,6 +694,7 @@
             sRegistry.notifyDisplayInfoChanged(slotIndex, subscriptionId, telephonyDisplayInfo);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -683,6 +709,7 @@
             sRegistry.notifyImsDisconnectCause(subId, imsReasonInfo);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -698,6 +725,7 @@
             sRegistry.notifySrvccStateChanged(subId, state);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -721,6 +749,7 @@
                 foregroundCallPreciseState, backgroundCallPreciseState);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -741,6 +770,7 @@
             sRegistry.notifyDisconnectCause(slotIndex, subId, cause, preciseCause);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -755,6 +785,7 @@
             sRegistry.notifyCellLocationForSubscriber(subId, cellLocation);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -769,7 +800,7 @@
         try {
             sRegistry.notifyCellInfoForSubscriber(subId, cellInfo);
         } catch (RemoteException ex) {
-
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -781,7 +812,7 @@
         try {
             sRegistry.notifyActiveDataSubIdChanged(activeDataSubId);
         } catch (RemoteException ex) {
-
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -814,6 +845,7 @@
             sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
                     chosenPlmn, domain, causeCode, additionalCauseCode);
         } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -830,6 +862,7 @@
             sRegistry.notifyBarringInfoChanged(slotIndex, subId, barringInfo);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -846,6 +879,7 @@
             sRegistry.notifyPhysicalChannelConfigForSubscriber(slotIndex, subId, configs);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -862,6 +896,7 @@
             sRegistry.notifyDataEnabled(slotIndex, subId, enabled, reason);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -880,6 +915,7 @@
                     allowedNetworkType);
         } catch (RemoteException ex) {
             // system process is dead
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -895,6 +931,7 @@
             sRegistry.notifyLinkCapacityEstimateChanged(slotIndex, subId, linkCapacityEstimateList);
         } catch (RemoteException ex) {
             // system server crash
+            throw ex.rethrowFromSystemServer();
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0f1c6f3..270b5d6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -25,6 +25,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;
@@ -36,7 +37,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;
@@ -136,7 +136,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;
@@ -11539,19 +11541,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;
     }
@@ -11561,7 +11555,8 @@
      * @param info The energy information from the WiFi controller.
      */
     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) {
             Slog.d(TAG, "Updating wifi stats: " + Arrays.toString(mWifiIfaces));
         }
@@ -11569,7 +11564,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());
@@ -11921,7 +11917,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);
         }
@@ -11935,7 +11932,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/jni/Android.bp b/core/jni/Android.bp
index 2cfec4b..07b16ed 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -242,6 +242,7 @@
                 "audioflinger-aidl-cpp",
                 "av-types-aidl-cpp",
                 "android.hardware.camera.device@3.2",
+                "libandroid_net",
                 "libandroidicu",
                 "libbpf_android",
                 "libnetdbpf",
diff --git a/core/jni/android_server_NetworkManagementSocketTagger.cpp b/core/jni/android_server_NetworkManagementSocketTagger.cpp
index afad08a..1be1873 100644
--- a/core/jni/android_server_NetworkManagementSocketTagger.cpp
+++ b/core/jni/android_server_NetworkManagementSocketTagger.cpp
@@ -15,24 +15,23 @@
  */
 
 #define LOG_TAG "NMST_QTagUidNative"
-#include <utils/Log.h>
 
-#include <nativehelper/JNIPlatformHelp.h>
-
-#include "jni.h"
-#include <utils/misc.h>
+#include <android/multinetwork.h>
 #include <cutils/qtaguid.h>
-
 #include <errno.h>
 #include <fcntl.h>
-#include <sys/types.h>
+#include <nativehelper/JNIPlatformHelp.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include "jni.h"
 
 namespace android {
 
-static jint QTagUid_tagSocketFd(JNIEnv* env, jclass,
-                                jobject fileDescriptor,
-                                jint tagNum, jint uid) {
+static jint tagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor,
+                        jint tagNum, jint uid) {
   int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
   if (env->ExceptionCheck()) {
@@ -40,15 +39,14 @@
     return (jint)-1;
   }
 
-  int res = qtaguid_tagSocket(userFd, tagNum, uid);
+  int res = android_tag_socket_with_uid(userFd, tagNum, uid);
   if (res < 0) {
     return (jint)-errno;
   }
   return (jint)res;
 }
 
-static jint QTagUid_untagSocketFd(JNIEnv* env, jclass,
-                                  jobject fileDescriptor) {
+static jint untagSocketFd(JNIEnv* env, jclass, jobject fileDescriptor) {
   int userFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
   if (env->ExceptionCheck()) {
@@ -56,16 +54,14 @@
     return (jint)-1;
   }
 
-  int res = qtaguid_untagSocket(userFd);
+  int res = android_untag_socket(userFd);
   if (res < 0) {
     return (jint)-errno;
   }
   return (jint)res;
 }
 
-static jint QTagUid_setCounterSet(JNIEnv* env, jclass,
-                                  jint setNum, jint uid) {
-
+static jint setCounterSet(JNIEnv* env, jclass, jint setNum, jint uid) {
   int res = qtaguid_setCounterSet(setNum, uid);
   if (res < 0) {
     return (jint)-errno;
@@ -73,9 +69,7 @@
   return (jint)res;
 }
 
-static jint QTagUid_deleteTagData(JNIEnv* env, jclass,
-                                  jint tagNum, jint uid) {
-
+static jint deleteTagData(JNIEnv* env, jclass, jint tagNum, jint uid) {
   int res = qtaguid_deleteTagData(tagNum, uid);
   if (res < 0) {
     return (jint)-errno;
@@ -84,10 +78,10 @@
 }
 
 static const JNINativeMethod gQTagUidMethods[] = {
-  { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)QTagUid_tagSocketFd},
-  { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)QTagUid_untagSocketFd},
-  { "native_setCounterSet", "(II)I", (void*)QTagUid_setCounterSet},
-  { "native_deleteTagData", "(II)I", (void*)QTagUid_deleteTagData},
+  { "native_tagSocketFd", "(Ljava/io/FileDescriptor;II)I", (void*)tagSocketFd},
+  { "native_untagSocketFd", "(Ljava/io/FileDescriptor;)I", (void*)untagSocketFd},
+  { "native_setCounterSet", "(II)I", (void*)setCounterSet},
+  { "native_deleteTagData", "(II)I", (void*)deleteTagData},
 };
 
 int register_android_server_NetworkManagementSocketTagger(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6ecfcec..c11502f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2114,8 +2114,9 @@
     <permission android:name="android.permission.NFC_HANDOVER_STATUS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @hide Allows internal management of Bluetooth state when on wireless consent mode.
-         <p>Not for use by third-party applications. -->
+    <!-- @SystemApi Allows internal management of Bluetooth state when on wireless consent mode.
+         <p>Not for use by third-party applications.
+         @hide -->
     <permission android:name="android.permission.MANAGE_BLUETOOTH_WHEN_WIRELESS_CONSENT_REQUIRED"
         android:protectionLevel="signature" />
 
@@ -6234,6 +6235,11 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.SmartStorageMaintIdler"
+                android:exported="true"
+                android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.ZramWriteback"
                  android:exported="false"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
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 48a1da1..1cc1894 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;
@@ -40,12 +41,15 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 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()
@@ -90,7 +94,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);
 
@@ -150,7 +155,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);
 
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 cee1a03..db0c934 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
 import android.net.NetworkStats;
 import android.os.Handler;
 import android.os.Looper;
@@ -105,7 +107,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/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java
new file mode 100644
index 0000000..2a47a02
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataRequest.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An object representing a request for credential data.
+ */
+public class CredentialDataRequest {
+    CredentialDataRequest() {}
+
+    /**
+     * Gets the device-signed entries to request.
+     *
+     * @return the device-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getDeviceSignedEntriesToRequest() {
+        return mDeviceSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets the issuer-signed entries to request.
+     *
+     * @return the issuer-signed entries to request.
+     */
+    public @NonNull Map<String, Collection<String>> getIssuerSignedEntriesToRequest() {
+        return mIssuerSignedEntriesToRequest;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which use count has been exceeded.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to allow using an authentication key which use
+     *         count has been exceeded if no other key is available.
+     */
+    public boolean isAllowUsingExhaustedKeys() {
+        return mAllowUsingExhaustedKeys;
+    }
+
+    /**
+     * Gets whether to allow using an authentication key which is expired.
+     *
+     * <p>By default this is set to false.
+     *
+     * @return whether to allow using an authentication key which is
+     *         expired if no other key is available.
+     */
+    public boolean isAllowUsingExpiredKeys() {
+        return mAllowUsingExpiredKeys;
+    }
+
+    /**
+     * Gets whether to increment the use-count for the authentication key used.
+     *
+     * <p>By default this is set to true.
+     *
+     * @return whether to increment the use count of the authentication key used.
+     */
+    public boolean isIncrementUseCount() {
+        return mIncrementUseCount;
+    }
+
+    /**
+     * Gets the request message CBOR.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return the request message CBOR as described above.
+     */
+    public @Nullable byte[] getRequestMessage() {
+        return mRequestMessage;
+    }
+
+    /**
+     * Gets the reader signature.
+     *
+     * <p>This data structure is described in the documentation for the
+     * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+     *
+     * @return a {@code COSE_Sign1} structure as described above.
+     */
+    public @Nullable byte[] getReaderSignature() {
+        return mReaderSignature;
+    }
+
+    Map<String, Collection<String>> mDeviceSignedEntriesToRequest = new LinkedHashMap<>();
+    Map<String, Collection<String>> mIssuerSignedEntriesToRequest = new LinkedHashMap<>();
+    boolean mAllowUsingExhaustedKeys = true;
+    boolean mAllowUsingExpiredKeys = false;
+    boolean mIncrementUseCount = true;
+    byte[] mRequestMessage = null;
+    byte[] mReaderSignature = null;
+
+    /**
+     * A builder for {@link CredentialDataRequest}.
+     */
+    public static final class Builder {
+        private CredentialDataRequest mData;
+
+        /**
+         * Creates a new builder.
+         */
+        public Builder() {
+            mData = new CredentialDataRequest();
+        }
+
+        /**
+         * Sets the device-signed entries to request.
+         *
+         * @param entriesToRequest the device-signed entries to request.
+         */
+        public @NonNull Builder setDeviceSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mDeviceSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets the issuer-signed entries to request.
+         *
+         * @param entriesToRequest the issuer-signed entries to request.
+         * @return the builder.
+         */
+        public @NonNull Builder setIssuerSignedEntriesToRequest(
+                @NonNull Map<String, Collection<String>> entriesToRequest) {
+            mData.mIssuerSignedEntriesToRequest = entriesToRequest;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which use count has been exceeded.
+         *
+         * By default this is set to true.
+         *
+         * @param allowUsingExhaustedKeys whether to allow using an authentication key which use
+         *                                count has been exceeded if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+            mData.mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to allow using an authentication key which is expired.
+         *
+         * By default this is set to false.
+         *
+         * @param allowUsingExpiredKeys whether to allow using an authentication key which is
+         *                              expired if no other key is available.
+         * @return the builder.
+         */
+        public @NonNull Builder setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+            mData.mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+            return this;
+        }
+
+        /**
+         * Sets whether to increment the use-count for the authentication key used.
+         *
+         * By default this is set to true.
+         *
+         * @param incrementUseCount whether to increment the use count of the authentication
+         *                          key used.
+         * @return the builder.
+         */
+        public @NonNull Builder setIncrementUseCount(boolean incrementUseCount) {
+            mData.mIncrementUseCount = incrementUseCount;
+            return this;
+        }
+
+        /**
+         * Sets the request message CBOR.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param requestMessage the request message CBOR as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setRequestMessage(@NonNull byte[] requestMessage) {
+            mData.mRequestMessage = requestMessage;
+            return this;
+        }
+
+        /**
+         * Sets the reader signature.
+         *
+         * <p>This data structure is described in the documentation for the
+         * {@link PresentationSession#getCredentialData(String, CredentialDataRequest)} method.
+         *
+         * @param readerSignature a {@code COSE_Sign1} structure as described above.
+         * @return the builder.
+         */
+        public @NonNull Builder setReaderSignature(@NonNull byte[] readerSignature) {
+            mData.mReaderSignature = readerSignature;
+            return this;
+        }
+
+        /**
+         * Finishes building a {@link CredentialDataRequest}.
+         *
+         * @return the {@link CredentialDataRequest} object.
+         */
+        public @NonNull CredentialDataRequest build() {
+            return mData;
+        }
+    }
+}
diff --git a/identity/java/android/security/identity/CredentialDataResult.java b/identity/java/android/security/identity/CredentialDataResult.java
new file mode 100644
index 0000000..beb03af
--- /dev/null
+++ b/identity/java/android/security/identity/CredentialDataResult.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.util.Collection;
+
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested in a {@link PresentationSession}.
+ */
+public abstract class CredentialDataResult {
+    /**
+     * @hide
+     */
+    protected CredentialDataResult() {}
+
+    /**
+     * Returns a CBOR structure containing the retrieved device-signed data.
+     *
+     * <p>This structure - along with the session transcript - may be cryptographically
+     * authenticated to prove to the reader that the data is from a trusted credential and
+     * {@link #getDeviceMac()} can be used to get a MAC.
+     *
+     * <p>The CBOR structure which is cryptographically authenticated is the
+     * {@code DeviceAuthenticationBytes} structure according to the following
+     * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
+     *
+     * <pre>
+     *   DeviceAuthentication = [
+     *     "DeviceAuthentication",
+     *     SessionTranscript,
+     *     DocType,
+     *     DeviceNameSpacesBytes
+     *   ]
+     *
+     *   DocType = tstr
+     *   SessionTranscript = any
+     *   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+     *   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+     * </pre>
+     *
+     * <p>where
+     *
+     * <pre>
+     *   DeviceNameSpaces = {
+     *     * NameSpace => DeviceSignedItems
+     *   }
+     *
+     *   DeviceSignedItems = {
+     *     + DataItemName => DataItemValue
+     *   }
+     *
+     *   NameSpace = tstr
+     *   DataItemName = tstr
+     *   DataItemValue = any
+     * </pre>
+     *
+     * <p>The returned data is the binary encoding of the {@code DeviceNameSpaces} structure
+     * as defined above.
+     *
+     * @return The bytes of the {@code DeviceNameSpaces} CBOR structure.
+     */
+    public abstract @NonNull byte[] getDeviceNameSpaces();
+
+    /**
+     * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
+     * specified in {@link #getDeviceNameSpaces()}, to prove to the reader that the data
+     * is from a trusted credential.
+     *
+     * <p>The MAC proves to the reader that the data is from a trusted credential. This code is
+     * produced by using the key agreement and key derivation function from the ciphersuite
+     * with the authentication private key and the reader ephemeral public key to compute a
+     * shared message authentication code (MAC) key, then using the MAC function from the
+     * ciphersuite to compute a MAC of the authenticated data. See section 9.2.3.5 of
+     * ISO/IEC 18013-5 for details of this operation.
+     *
+     * <p>If the session transcript or reader ephemeral key wasn't set on the {@link
+     * PresentationSession} used to obtain this data no message authencation code will be produced
+     * and this method will return {@code null}.
+     *
+     * @return A COSE_Mac0 structure with the message authentication code as described above
+     *         or {@code null} if the conditions specified above are not met.
+     */
+    public abstract @Nullable byte[] getDeviceMac();
+
+    /**
+     * Returns the static authentication data associated with the dynamic authentication
+     * key used to MAC the data returned by {@link #getDeviceNameSpaces()}.
+     *
+     * @return The static authentication data associated with dynamic authentication key used to
+     * MAC the data.
+     */
+    public abstract @NonNull byte[] getStaticAuthenticationData();
+
+    /**
+     * Gets the device-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getDeviceSignedEntries();
+
+    /**
+     * Gets the issuer-signed entries that was returned.
+     *
+     * @return an object to examine the entries returned.
+     */
+    public abstract @NonNull Entries getIssuerSignedEntries();
+
+    /**
+     * A class for representing data elements returned.
+     */
+    public interface Entries {
+        /** Value was successfully retrieved. */
+        int STATUS_OK = 0;
+
+        /** The entry does not exist. */
+        int STATUS_NO_SUCH_ENTRY = 1;
+
+        /** The entry was not requested. */
+        int STATUS_NOT_REQUESTED = 2;
+
+        /** The entry wasn't in the request message. */
+        int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+        /** The entry was not retrieved because user authentication failed. */
+        int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+        /** The entry was not retrieved because reader authentication failed. */
+        int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+        /**
+         * The entry was not retrieved because it was configured without any access
+         * control profile.
+         */
+        int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+        /**
+         * Gets the names of namespaces with retrieved entries.
+         *
+         * @return collection of name of namespaces containing retrieved entries. May be empty if no
+         *     data was retrieved.
+         */
+        @NonNull Collection<String> getNamespaces();
+
+        /**
+         * Get the names of all requested entries in a name space.
+         *
+         * <p>This includes the name of entries that wasn't successfully retrieved.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return A collection of names for the given namespace or the empty collection if no
+         *   entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Get the names of all entries that was successfully retrieved from a name space.
+         *
+         * <p>This only return entries for which {@link #getStatus(String, String)} will return
+         * {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name to get entries for.
+         * @return The entries in the given namespace that were successfully rerieved or the
+         *   empty collection if no entries was returned for the given name space.
+         */
+        @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName);
+
+        /**
+         * Gets the status of an entry.
+         *
+         * <p>This returns {@link #STATUS_OK} if the value was retrieved, {@link
+         * #STATUS_NO_SUCH_ENTRY} if the given entry wasn't retrieved, {@link
+         * #STATUS_NOT_REQUESTED} if it wasn't requested, {@link #STATUS_NOT_IN_REQUEST_MESSAGE} if
+         * the request message was set but the entry wasn't present in the request message, {@link
+         * #STATUS_USER_AUTHENTICATION_FAILED} if the value wasn't retrieved because the necessary
+         * user authentication wasn't performed, {@link #STATUS_READER_AUTHENTICATION_FAILED} if
+         * the supplied reader certificate chain didn't match the set of certificates the entry was
+         * provisioned with, or {@link #STATUS_NO_ACCESS_CONTROL_PROFILES} if the entry was
+         * configured without any access control profiles.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the status indicating whether the value was retrieved and if not, why.
+         */
+        @Status int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * Gets the raw CBOR data for the value of an entry.
+         *
+         * <p>This should only be called on an entry for which the {@link #getStatus(String,
+         * String)} method returns {@link #STATUS_OK}.
+         *
+         * @param namespaceName the namespace name of the entry.
+         * @param name the name of the entry to get the value for.
+         * @return the raw CBOR data or {@code null} if no entry with the given name exists.
+         */
+        @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name);
+
+        /**
+         * The type of the entry status.
+         * @hide
+         */
+        @Retention(SOURCE)
+        @IntDef({STATUS_OK, STATUS_NO_SUCH_ENTRY, STATUS_NOT_REQUESTED,
+                    STATUS_NOT_IN_REQUEST_MESSAGE, STATUS_USER_AUTHENTICATION_FAILED,
+                    STATUS_READER_AUTHENTICATION_FAILED, STATUS_NO_ACCESS_CONTROL_PROFILES})
+                    @interface Status {}
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreCredentialDataResult.java b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
new file mode 100644
index 0000000..7afe3d4
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreCredentialDataResult.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreCredentialDataResult extends CredentialDataResult {
+
+    ResultData mDeviceSignedResult;
+    ResultData mIssuerSignedResult;
+    CredstoreEntries mDeviceSignedEntries;
+    CredstoreEntries mIssuerSignedEntries;
+
+    CredstoreCredentialDataResult(ResultData deviceSignedResult, ResultData issuerSignedResult) {
+        mDeviceSignedResult = deviceSignedResult;
+        mIssuerSignedResult = issuerSignedResult;
+        mDeviceSignedEntries = new CredstoreEntries(deviceSignedResult);
+        mIssuerSignedEntries = new CredstoreEntries(issuerSignedResult);
+    }
+
+    @Override
+    public @NonNull byte[] getDeviceNameSpaces() {
+        return mDeviceSignedResult.getAuthenticatedData();
+    }
+
+    @Override
+    public @Nullable byte[] getDeviceMac() {
+        return mDeviceSignedResult.getMessageAuthenticationCode();
+    }
+
+    @Override
+    public @NonNull byte[] getStaticAuthenticationData() {
+        return mDeviceSignedResult.getStaticAuthenticationData();
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getDeviceSignedEntries() {
+        return mDeviceSignedEntries;
+    }
+
+    @Override
+    public @NonNull CredentialDataResult.Entries getIssuerSignedEntries() {
+        return mIssuerSignedEntries;
+    }
+
+    static class CredstoreEntries implements CredentialDataResult.Entries {
+        ResultData mResultData;
+
+        CredstoreEntries(ResultData resultData) {
+            mResultData = resultData;
+        }
+
+        @Override
+        public @NonNull Collection<String> getNamespaces() {
+            return mResultData.getNamespaces();
+        }
+
+        @Override
+        public @NonNull Collection<String> getEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        public @NonNull Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+            Collection<String> ret = mResultData.getRetrievedEntryNames(namespaceName);
+            if (ret == null) {
+                ret = new LinkedList<String>();
+            }
+            return ret;
+        }
+
+        @Override
+        @Status
+        public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getStatus(namespaceName, name);
+        }
+
+        @Override
+        public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+            return mResultData.getEntry(namespaceName, name);
+        }
+    }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 6398cee..8e01105 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -58,14 +58,17 @@
     private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
     private Context mContext;
     private ICredential mBinder;
+    private CredstorePresentationSession mSession;
 
     CredstoreIdentityCredential(Context context, String credentialName,
             @IdentityCredentialStore.Ciphersuite int cipherSuite,
-            ICredential binder) {
+            ICredential binder,
+            @Nullable CredstorePresentationSession session) {
         mContext = context;
         mCredentialName = credentialName;
         mCipherSuite = cipherSuite;
         mBinder = binder;
+        mSession = session;
     }
 
     private KeyPair mEphemeralKeyPair = null;
@@ -239,6 +242,7 @@
 
     private boolean mAllowUsingExhaustedKeys = true;
     private boolean mAllowUsingExpiredKeys = false;
+    private boolean mIncrementKeyUsageCount = true;
 
     @Override
     public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
@@ -250,6 +254,11 @@
         mAllowUsingExpiredKeys = allowUsingExpiredKeys;
     }
 
+    @Override
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        mIncrementKeyUsageCount = incrementKeyUsageCount;
+    }
+
     private boolean mOperationHandleSet = false;
     private long mOperationHandle = 0;
 
@@ -264,7 +273,8 @@
         if (!mOperationHandleSet) {
             try {
                 mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
-                        mAllowUsingExpiredKeys);
+                                                         mAllowUsingExpiredKeys,
+                                                         mIncrementKeyUsageCount);
                 mOperationHandleSet = true;
             } catch (android.os.RemoteException e) {
                 throw new RuntimeException("Unexpected RemoteException ", e);
@@ -315,7 +325,8 @@
                 sessionTranscript != null ? sessionTranscript : new byte[0],
                 readerSignature != null ? readerSignature : new byte[0],
                 mAllowUsingExhaustedKeys,
-                mAllowUsingExpiredKeys);
+                mAllowUsingExpiredKeys,
+                mIncrementKeyUsageCount);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index d8d4742..fb0880c 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -126,7 +126,8 @@
             ICredential credstoreCredential;
             credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
             return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
-                    credstoreCredential);
+                    credstoreCredential,
+                    null);
         } catch (android.os.RemoteException e) {
             throw new RuntimeException("Unexpected RemoteException ", e);
         } catch (android.os.ServiceSpecificException e) {
@@ -162,4 +163,23 @@
                     + e.errorCode, e);
         }
     }
+
+    @Override
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        try {
+            ISession credstoreSession = mStore.createPresentationSession(cipherSuite);
+            return new CredstorePresentationSession(mContext, cipherSuite, this, credstoreSession);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_CIPHER_SUITE_NOT_SUPPORTED) {
+                throw new CipherSuiteNotSupportedException(e.getMessage(), e);
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
 }
diff --git a/identity/java/android/security/identity/CredstorePresentationSession.java b/identity/java/android/security/identity/CredstorePresentationSession.java
new file mode 100644
index 0000000..e3c6689
--- /dev/null
+++ b/identity/java/android/security/identity/CredstorePresentationSession.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+class CredstorePresentationSession extends PresentationSession {
+    private static final String TAG = "CredstorePresentationSession";
+
+    private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+    private Context mContext;
+    private CredstoreIdentityCredentialStore mStore;
+    private ISession mBinder;
+    private Map<String, CredstoreIdentityCredential> mCredentialCache = new LinkedHashMap<>();
+    private KeyPair mEphemeralKeyPair = null;
+    private byte[] mSessionTranscript = null;
+    private boolean mOperationHandleSet = false;
+    private long mOperationHandle = 0;
+
+    CredstorePresentationSession(Context context,
+            @IdentityCredentialStore.Ciphersuite int cipherSuite,
+            CredstoreIdentityCredentialStore store,
+            ISession binder) {
+        mContext = context;
+        mCipherSuite = cipherSuite;
+        mStore = store;
+        mBinder = binder;
+    }
+
+    private void ensureEphemeralKeyPair() {
+        if (mEphemeralKeyPair != null) {
+            return;
+        }
+        try {
+            // This PKCS#12 blob is generated in credstore, using BoringSSL.
+            //
+            // The main reason for this convoluted approach and not just sending the decomposed
+            // key-pair is that this would require directly using (device-side) BouncyCastle which
+            // is tricky due to various API hiding efforts. So instead we have credstore generate
+            // this PKCS#12 blob. The blob is encrypted with no password (sadly, also, BoringSSL
+            // doesn't support not using encryption when building a PKCS#12 blob).
+            //
+            byte[] pkcs12 = mBinder.getEphemeralKeyPair();
+            String alias = "ephemeralKey";
+            char[] password = {};
+
+            KeyStore ks = KeyStore.getInstance("PKCS12");
+            ByteArrayInputStream bais = new ByteArrayInputStream(pkcs12);
+            ks.load(bais, password);
+            PrivateKey privKey = (PrivateKey) ks.getKey(alias, password);
+
+            Certificate cert = ks.getCertificate(alias);
+            PublicKey pubKey = cert.getPublicKey();
+
+            mEphemeralKeyPair = new KeyPair(pubKey, privKey);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        } catch (android.os.RemoteException
+                | KeyStoreException
+                | CertificateException
+                | UnrecoverableKeyException
+                | NoSuchAlgorithmException
+                | IOException e) {
+            throw new RuntimeException("Unexpected exception ", e);
+        }
+    }
+
+    @Override
+    public @NonNull KeyPair getEphemeralKeyPair() {
+        ensureEphemeralKeyPair();
+        return mEphemeralKeyPair;
+    }
+
+    @Override
+    public void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException {
+        try {
+            byte[] uncompressedForm =
+                    Util.publicKeyEncodeUncompressedForm(readerEphemeralPublicKey);
+            mBinder.setReaderEphemeralPublicKey(uncompressedForm);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public void setSessionTranscript(@NonNull byte[] sessionTranscript) {
+        try {
+            mBinder.setSessionTranscript(sessionTranscript);
+            mSessionTranscript = sessionTranscript;
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                    + e.errorCode, e);
+        }
+    }
+
+    @Override
+    public @Nullable CredentialDataResult getCredentialData(@NonNull String credentialName,
+                                                            @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException {
+        try {
+            // Cache the IdentityCredential to satisfy the property that AuthKey usage counts are
+            // incremented on only the _first_ getCredentialData() call.
+            //
+            CredstoreIdentityCredential credential = mCredentialCache.get(credentialName);
+            if (credential == null) {
+                ICredential credstoreCredential =
+                    mBinder.getCredentialForPresentation(credentialName);
+                credential = new CredstoreIdentityCredential(mContext, credentialName,
+                                                             mCipherSuite, credstoreCredential,
+                                                             this);
+                mCredentialCache.put(credentialName, credential);
+
+                credential.setAllowUsingExhaustedKeys(request.isAllowUsingExhaustedKeys());
+                credential.setAllowUsingExpiredKeys(request.isAllowUsingExpiredKeys());
+                credential.setIncrementKeyUsageCount(request.isIncrementUseCount());
+            }
+
+            ResultData deviceSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getDeviceSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            // By design this second getEntries() call consumes the same auth-key.
+
+            ResultData issuerSignedResult = credential.getEntries(
+                    request.getRequestMessage(),
+                    request.getIssuerSignedEntriesToRequest(),
+                    mSessionTranscript,
+                    request.getReaderSignature());
+
+            return new CredstoreCredentialDataResult(deviceSignedResult, issuerSignedResult);
+
+        } catch (SessionTranscriptMismatchException e) {
+            throw new RuntimeException("Unexpected ", e);
+        } catch (android.os.RemoteException e) {
+            throw new RuntimeException("Unexpected RemoteException ", e);
+        } catch (android.os.ServiceSpecificException e) {
+            if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+                return null;
+            } else {
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+    }
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    @Override
+    public long getCredstoreOperationHandle() {
+        if (!mOperationHandleSet) {
+            try {
+                mOperationHandle = mBinder.getAuthChallenge();
+                mOperationHandleSet = true;
+            } catch (android.os.RemoteException e) {
+                throw new RuntimeException("Unexpected RemoteException ", e);
+            } catch (android.os.ServiceSpecificException e) {
+                if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+                    // The NoAuthenticationKeyAvailableException will be thrown when
+                    // the caller proceeds to call getEntries().
+                }
+                throw new RuntimeException("Unexpected ServiceSpecificException with code "
+                        + e.errorCode, e);
+            }
+        }
+        return mOperationHandle;
+    }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 1e68585..cdf746f 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -48,7 +48,9 @@
      * encryption".
      *
      * @return ephemeral key pair to use to establish a secure channel with a reader.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public @NonNull abstract KeyPair createEphemeralKeyPair();
 
     /**
@@ -58,7 +60,9 @@
      * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
      *                                 establish a secure session.
      * @throws InvalidKeyException if the given key is invalid.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
             throws InvalidKeyException;
 
@@ -72,7 +76,10 @@
      *
      * @param messagePlaintext unencrypted message to encrypt.
      * @return encrypted message.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
 
     /**
@@ -86,7 +93,10 @@
      * @param messageCiphertext encrypted message to decrypt.
      * @return decrypted message.
      * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+     * @deprecated Applications should use {@link PresentationSession} and
+     *             implement encryption/decryption themselves.
      */
+    @Deprecated
     public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
             throws MessageDecryptionException;
 
@@ -111,7 +121,9 @@
      *
      * @param allowUsingExhaustedKeys whether to allow using an authentication key which use count
      *                                has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
 
     /**
@@ -128,12 +140,36 @@
      *
      * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
      *                              has been exceeded if no other key is available.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
         throw new UnsupportedOperationException();
     }
 
     /**
+     * @hide
+     *
+     * Sets whether the usage count of an authentication key should be increased. This must be
+     * called prior to calling
+     * {@link #getEntries(byte[], Map, byte[], byte[])} or using a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this object.
+     *
+     * <p>By default this is set to true.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param incrementKeyUsageCount whether the usage count of the key should be increased.
+     * @deprecated Use {@link PresentationSession} instead.
+     */
+    public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
      * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
      * operation handle.
      *
@@ -149,15 +185,19 @@
      * by using the {@link ResultData#getStatus(String, String)} method on each of the requested
      * entries.
      *
-     * <p>It is the responsibility of the calling application to know if authentication is needed
-     * and use e.g. {@link android.hardware.biometrics.BiometricPrompt} to make the user
-     * authenticate using a {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which
-     * references this object. If needed, this must be done before calling
-     * {@link #getEntries(byte[], Map, byte[], byte[])}.
-     *
      * <p>It is permissible to call this method multiple times using the same instance but if this
      * is done, the {@code sessionTranscript} parameter must be identical for each call. If this is
      * not the case, the {@link SessionTranscriptMismatchException} exception is thrown.
+     * Additionally, if this is done the same auth-key will be used.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link ResultData}. If {@link ResultData#STATUS_USER_AUTHENTICATION_FAILED}
+     * is returned the application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call {@link #getEntries(byte[], Map, byte[], byte[])}
+     * again.
      *
      * <p>If not {@code null} the {@code requestMessage} parameter must contain data for the request
      * from the verifier. The content can be defined in the way appropriate for the credential, but
@@ -269,7 +309,9 @@
      * @throws InvalidRequestMessageException         if the requestMessage is malformed.
      * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
      *                                                the session transcript.
+     * @deprecated Use {@link PresentationSession} instead.
      */
+    @Deprecated
     public abstract @NonNull ResultData getEntries(
             @Nullable byte[] requestMessage,
             @NonNull Map<String, Collection<String>> entriesToRequest,
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 6ccd0e8..dbb8aaa 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -209,6 +209,25 @@
     @Deprecated
     public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
 
+    /**
+     * Creates a new presentation session.
+     *
+     * <p>This method gets an object to be used for interaction with a remote verifier for
+     * presentation of one or more credentials.
+     *
+     * <p>This is only implemented in feature version 202201 or later. If not implemented, the call
+     * fails with {@link UnsupportedOperationException}. See
+     * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+     * feature versions.
+     *
+     * @param cipherSuite    the cipher suite to use for communicating with the verifier.
+     * @return The presentation session.
+     */
+    public @NonNull PresentationSession createPresentationSession(@Ciphersuite int cipherSuite)
+            throws CipherSuiteNotSupportedException {
+        throw new UnsupportedOperationException();
+    }
+
     /** @hide */
     @IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
     @Retention(RetentionPolicy.SOURCE)
diff --git a/identity/java/android/security/identity/PresentationSession.java b/identity/java/android/security/identity/PresentationSession.java
new file mode 100644
index 0000000..afaafce
--- /dev/null
+++ b/identity/java/android/security/identity/PresentationSession.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+
+/**
+ * Class for presenting multiple documents to a remote verifier.
+ *
+ * Use {@link IdentityCredentialStore#createPresentationSession(int)} to create a {@link
+ * PresentationSession} instance.
+ */
+public abstract class PresentationSession {
+    /**
+     * @hide
+     */
+    protected PresentationSession() {}
+
+    /**
+     * Gets the ephemeral key pair to use to establish a secure channel with the verifier.
+     *
+     * <p>Applications should use this key-pair for the communications channel with the verifier
+     * using a protocol / cipher-suite appropriate for the application. One example of such a
+     * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5.
+     *
+     * <p>The ephemeral key pair is tied to the {@link PresentationSession} instance so subsequent
+     * calls to this method will return the same key-pair.
+     *
+     * @return ephemeral key pair to use to establish a secure channel with a reader.
+     */
+    public @NonNull abstract KeyPair getEphemeralKeyPair();
+
+    /**
+     * Set the ephemeral public key provided by the verifier.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
+     *                                 establish a secure session.
+     * @throws InvalidKeyException if the given key is invalid.
+     */
+    public abstract void setReaderEphemeralPublicKey(@NonNull PublicKey readerEphemeralPublicKey)
+            throws InvalidKeyException;
+
+    /**
+     * Set the session transcript.
+     *
+     * <p>If called, this must be called before any calls to
+     * {@link #getCredentialData(String, CredentialDataRequest)}.
+     *
+     * <p>The X and Y coordinates of the public part of the key-pair returned by {@link
+     * #getEphemeralKeyPair()} must appear somewhere in the bytes of the passed in CBOR.  Each of
+     * these coordinates must appear encoded with the most significant bits first and use the exact
+     * amount of bits indicated by the key size of the ephemeral keys. For example, if the
+     * ephemeral key is using the P-256 curve then the 32 bytes for the X coordinate encoded with
+     * the most significant bits first must appear somewhere and ditto for the 32 bytes for the Y
+     * coordinate.
+     *
+     * <p>This method can only be called once per {@link PresentationSession} instance.
+     *
+     * @param sessionTranscript the session transcript.
+     */
+    public abstract void setSessionTranscript(@NonNull byte[] sessionTranscript);
+
+    /**
+     * Retrieves data from a named credential in the current presentation session.
+     *
+     * <p>If an access control check fails for one of the requested entries or if the entry
+     * doesn't exist, the entry is simply not returned. The application can detect this
+     * by using the {@link CredentialDataResult.Entries#getStatus(String, String)} method on
+     * each of the requested entries.
+     *
+     * <p>The application should not make any assumptions on whether user authentication is needed.
+     * Instead, the application should request the data elements values first and then examine
+     * the returned {@link CredentialDataResult.Entries}. If
+     * {@link CredentialDataResult.Entries#STATUS_USER_AUTHENTICATION_FAILED} is returned the
+     * application should get a
+     * {@link android.hardware.biometrics.BiometricPrompt.CryptoObject} which references this
+     * object and use it with a {@link android.hardware.biometrics.BiometricPrompt}. Upon successful
+     * authentication the application may call
+     * {@link #getCredentialData(String, CredentialDataRequest)} again.
+     *
+     * <p>It is permissible to call this method multiple times using the same credential name.
+     * If this is done the same auth-key will be used.
+     *
+     * <p>If the reader signature is set in the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setReaderSignature(byte[])} method) it must contain
+     * the bytes of a {@code COSE_Sign1} structure as defined in RFC 8152. For the payload
+     * {@code nil} shall be used and the detached payload is the {@code ReaderAuthenticationBytes}
+     * CBOR described below.
+     * <pre>
+     *     ReaderAuthentication = [
+     *       "ReaderAuthentication",
+     *       SessionTranscript,
+     *       ItemsRequestBytes
+     *     ]
+     *
+     *     ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+     *
+     *     ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+     * </pre>
+     *
+     * <p>where {@code ItemsRequestBytes} are the bytes of the request message set in
+     * the request parameter (via the
+     * {@link CredentialDataRequest.Builder#setRequestMessage(byte[])} method).
+     *
+     * <p>The public key corresponding to the key used to make the signature, can be found in the
+     * {@code x5chain} unprotected header element of the {@code COSE_Sign1} structure (as as
+     * described in
+     * <a href="https://tools.ietf.org/html/draft-ietf-cose-x509-08">draft-ietf-cose-x509-08</a>).
+     * There will be at least one certificate in said element and there may be more (and if so,
+     * each certificate must be signed by its successor).
+     *
+     * <p>Data elements protected by reader authentication are returned if, and only if,
+     * {@code requestMessage} is signed by the top-most certificate in the reader's certificate
+     * chain, and the data element is configured with an {@link AccessControlProfile} configured
+     * with an X.509 certificate for a key which appear in the certificate chain.
+     *
+     * <p>Note that the request message CBOR is used only for enforcing reader authentication, it's
+     * not used for determining which entries this API will return. The application is expected to
+     * have parsed the request message and filtered it according to user preference and/or consent.
+     *
+     * @param credentialName the name of the credential to retrieve.
+     * @param request the data to retrieve from the credential
+     * @return If the credential wasn't found, returns null. Otherwise a
+     *         {@link CredentialDataResult} object containing entry data organized by namespace and
+     *         a cryptographically authenticated representation of the same data, bound to the
+     *         current session.
+     * @throws NoAuthenticationKeyAvailableException  if authentication keys were never
+     *                                                provisioned for the credential or if they
+     *                                                are expired or exhausted their use-count.
+     * @throws InvalidRequestMessageException         if the requestMessage is malformed.
+     * @throws InvalidReaderSignatureException        if the reader signature is invalid, or it
+     *                                                doesn't contain a certificate chain, or if
+     *                                                the signature failed to validate.
+     * @throws EphemeralPublicKeyNotFoundException    if the ephemeral public key was not found in
+     *                                                the session transcript.
+     */
+    public abstract @Nullable CredentialDataResult getCredentialData(
+            @NonNull String credentialName, @NonNull CredentialDataRequest request)
+            throws NoAuthenticationKeyAvailableException, InvalidReaderSignatureException,
+            InvalidRequestMessageException, EphemeralPublicKeyNotFoundException;
+
+    /**
+     * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+     * operation handle.
+     *
+     * @hide
+     */
+    public abstract long getCredstoreOperationHandle();
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 71860d2..d46f985 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -28,7 +28,10 @@
 /**
  * An object that contains the result of retrieving data from a credential. This is used to return
  * data requested from a {@link IdentityCredential}.
+ *
+ * @deprecated Use {@link PresentationSession} instead.
  */
+@Deprecated
 public abstract class ResultData {
 
     /** Value was successfully retrieved. */
diff --git a/libs/hwui/jni/NinePatch.cpp b/libs/hwui/jni/NinePatch.cpp
index 6942017..3ca457f 100644
--- a/libs/hwui/jni/NinePatch.cpp
+++ b/libs/hwui/jni/NinePatch.cpp
@@ -67,7 +67,7 @@
         size_t chunkSize = env->GetArrayLength(obj);
         if (chunkSize < (int) (sizeof(Res_png_9patch))) {
             jniThrowRuntimeException(env, "Array too small for chunk.");
-            return NULL;
+            return 0;
         }
 
         int8_t* storage = new int8_t[chunkSize];
diff --git a/libs/hwui/jni/android_util_PathParser.cpp b/libs/hwui/jni/android_util_PathParser.cpp
index 72995ef..8cbb70e 100644
--- a/libs/hwui/jni/android_util_PathParser.cpp
+++ b/libs/hwui/jni/android_util_PathParser.cpp
@@ -61,7 +61,7 @@
     } else {
         delete pathData;
         doThrowIAE(env, result.failureMessage.c_str());
-        return NULL;
+        return 0;
     }
 }
 
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java
index 19ea2de..d1bb41e 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BtProfileConnectionInfo.java
@@ -34,7 +34,7 @@
     /** @hide */
     @IntDef({
             BluetoothProfile.A2DP,
-            BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper
+            BluetoothProfile.A2DP_SINK,
             BluetoothProfile.HEADSET, // Can only be set by BtHelper
             BluetoothProfile.HEARING_AID,
             BluetoothProfile.LE_AUDIO,
@@ -105,6 +105,16 @@
     }
 
     /**
+     * Constructor for A2dp sink info
+     * The {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     *
+     * @param volume of device -1 to ignore value
+     */
+    public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
+        return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+    }
+
+    /**
      * Constructor for hearing aid info
      *
      * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
diff --git a/media/java/android/media/tv/OWNERS b/media/java/android/media/tv/OWNERS
index 33acd0d..fa04293 100644
--- a/media/java/android/media/tv/OWNERS
+++ b/media/java/android/media/tv/OWNERS
@@ -1,6 +1,6 @@
-nchalko@google.com
 quxiangfang@google.com
 shubang@google.com
+hgchen@google.com
 
 # For android remote service
 per-file ITvRemoteServiceInput.aidl = file:/media/lib/tvremote/OWNERS
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index efd9d10..420f4ef 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -27,6 +28,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat.Encoding;
 import android.media.PlaybackParams;
 import android.net.Uri;
 import android.os.Binder;
@@ -59,6 +62,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -2998,6 +3002,16 @@
             return false;
         }
 
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param audioType device type of the audio sink to override with.
+         * @param audioAddress device address of the audio sink to override with.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
         public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
                 int channelMask, int format) {
             try {
@@ -3007,5 +3021,27 @@
                 throw new RuntimeException(e);
             }
         }
+
+        /**
+         * Override default audio sink from audio policy.
+         *
+         * @param device {@link android.media.AudioDeviceInfo} to use.
+         * @param samplingRate desired sampling rate. Use default when it's 0.
+         * @param channelMask desired channel mask. Use default when it's
+         *        AudioFormat.CHANNEL_OUT_DEFAULT.
+         * @param format desired format. Use default when it's AudioFormat.ENCODING_DEFAULT.
+         */
+        public void overrideAudioSink(@NonNull AudioDeviceInfo device,
+                @IntRange(from = 0) int samplingRate,
+                int channelMask, @Encoding int format) {
+            Objects.requireNonNull(device);
+            try {
+                mInterface.overrideAudioSink(
+                        AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
+                        device.getAddress(), samplingRate, channelMask, format);
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/media/java/android/media/tv/tunerresourcemanager/OWNER b/media/java/android/media/tv/tunerresourcemanager/OWNER
index 76b84d9..0eb1c31 100644
--- a/media/java/android/media/tv/tunerresourcemanager/OWNER
+++ b/media/java/android/media/tv/tunerresourcemanager/OWNER
@@ -1,4 +1,3 @@
-amyjojo@google.com
-nchalko@google.com
 quxiangfang@google.com
-shubang@google.com
\ No newline at end of file
+shubang@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
index 73ea663..7554889 100644
--- a/media/tests/TunerTest/OWNERS
+++ b/media/tests/TunerTest/OWNERS
@@ -1,4 +1,4 @@
-amyjojo@google.com
-nchalko@google.com
 quxiangfang@google.com
 shubang@google.com
+hgchen@google.com
+kemiyagi@google.com
\ No newline at end of file
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index a6c1b50..32fd734 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -18,6 +18,10 @@
     android_getprocnetwork; # llndk
     android_setprocdns; # llndk
     android_getprocdns; # llndk
+    # These functions have been part of the NDK since API 33.
+    android_tag_socket_with_uid; # llndk
+    android_tag_socket;  # llndk
+    android_untag_socket; # llndk
   local:
     *;
 };
diff --git a/native/android/net.c b/native/android/net.c
index e2f36a7..d7c22e1 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -161,3 +161,15 @@
 void android_res_cancel(int nsend_fd) {
     resNetworkCancel(nsend_fd);
 }
+
+int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+    return tagSocket(sockfd, tag, uid);
+}
+
+int android_tag_socket(int sockfd, int tag) {
+    return tagSocket(sockfd, tag, -1);
+}
+
+int android_untag_socket(int sockfd) {
+    return untagSocket(sockfd);
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
index f684a4d..d33666d 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStats.java
@@ -545,6 +545,15 @@
     }
 
     /**
+     * Collects tagged summary results and sets summary enumeration mode.
+     * @throws RemoteException
+     */
+    void startTaggedSummaryEnumeration() throws RemoteException {
+        mSummary = mSession.getTaggedSummaryForAllUid(mTemplate, mStartTimeStamp, mEndTimeStamp);
+        mEnumerationIndex = 0;
+    }
+
+    /**
      * Collects history results for uid and resets history enumeration index.
      */
     void startHistoryEnumeration(int uid, int tag, int state) {
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..f74edb1 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
@@ -141,7 +142,15 @@
         setAugmentWithSubscriptionPlan(true);
     }
 
-    /** @hide */
+    /**
+     * Set poll on open flag to indicate the poll is needed before service gets statistics
+     * result. This is default enabled. However, for any non-privileged caller, the poll might
+     * be omitted in case of rate limiting.
+     *
+     * @param pollOnOpen true if poll is needed.
+     * @hide
+     */
+    // @SystemApi(client = MODULE_LIBRARIES)
     public void setPollOnOpen(boolean pollOnOpen) {
         if (pollOnOpen) {
             mFlags |= FLAG_POLL_ON_OPEN;
@@ -368,7 +377,7 @@
      * @return Statistics which is described above.
      * @hide
      */
-    @Nullable
+    @NonNull
     // @SystemApi(client = MODULE_LIBRARIES)
     @WorkerThread
     public NetworkStats querySummary(@NonNull NetworkTemplate template, long startTime,
@@ -385,6 +394,39 @@
     }
 
     /**
+     * Query tagged network usage statistics summaries.
+     *
+     * The results will only include tagged traffic made by UIDs belonging to the calling user
+     * profile. The results are aggregated over time, so that all buckets will have the same
+     * start and end timestamps as the passed arguments. Not aggregated over state, uid,
+     * default network, metered, or roaming.
+     * This may take a long time, and apps should avoid calling this on their main thread.
+     *
+     * @param template Template used to match networks. See {@link NetworkTemplate}.
+     * @param startTime Start of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @param endTime End of period, in milliseconds since the Unix epoch, see
+     *            {@link System#currentTimeMillis}.
+     * @return Statistics which is described above.
+     * @hide
+     */
+    @NonNull
+    // @SystemApi(client = MODULE_LIBRARIES)
+    @WorkerThread
+    public NetworkStats queryTaggedSummary(@NonNull NetworkTemplate template, long startTime,
+            long endTime) throws SecurityException {
+        try {
+            NetworkStats result =
+                    new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
+            result.startTaggedSummaryEnumeration();
+            return result;
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return null; // To make the compiler happy.
+    }
+
+    /**
      * Query network usage statistics details for a given uid.
      * This may take a long time, and apps should avoid calling this on their main thread.
      *
@@ -535,6 +577,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) {
@@ -804,4 +871,74 @@
             return msg.getData().getParcelable(key);
         }
     }
+
+    /**
+     * Mark given UID as being in foreground for stats purposes.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setUidForeground(int uid, boolean uidForeground) {
+        try {
+            mService.setUidForeground(uid, uidForeground);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Advise persistence threshold; may be overridden internally.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void advisePersistThreshold(long thresholdBytes) {
+        try {
+            mService.advisePersistThreshold(thresholdBytes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Force update of statistics.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void forceUpdate() {
+        try {
+            mService.forceUpdate();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     *
+     * @hide
+     */
+    // @SystemApi
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
+            long limit) {
+        try {
+            mService.setStatsProviderWarningAndLimitAsync(iface, warning, limit);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
index 12937b5..a4babb5 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsService.aidl
@@ -94,4 +94,16 @@
     /** Registers a network stats provider */
     INetworkStatsProviderCallback registerNetworkStatsProvider(String tag,
             in INetworkStatsProvider provider);
+
+    /** Mark given UID as being in foreground for stats purposes. */
+    void setUidForeground(int uid, boolean uidForeground);
+
+    /** Advise persistence threshold; may be overridden internally. */
+    void advisePersistThreshold(long thresholdBytes);
+
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+     void setStatsProviderWarningAndLimitAsync(String iface, long warning, long limit);
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
index dfedf66..babe0bf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/INetworkStatsSession.aidl
@@ -46,6 +46,10 @@
      */
     @UnsupportedAppUsage
     NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
+
+    /** Return network layer usage summary per UID for tagged traffic that matches template. */
+    NetworkStats getTaggedSummaryForAllUid(in NetworkTemplate template, long start, long end);
+
     /** Return historical network layer stats for specific UID traffic that matches template. */
     @UnsupportedAppUsage
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
index 0d15dff..49aa99b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
@@ -17,6 +17,7 @@
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
@@ -25,7 +26,6 @@
 import android.annotation.TestApi;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.annotations.PolicyDirection;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -41,6 +41,8 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -88,6 +90,11 @@
     @SystemApi(client = MODULE_LIBRARIES)
     public static final int DIRECTION_FWD = 2;
 
+    /** @hide */
+    @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PolicyDirection {}
+
     /**
      * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index.
      *
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 7935d28..9f9d73f 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -46,8 +46,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastDataInput;
-import com.android.internal.util.FastDataOutput;
 import com.android.internal.util.FileRotator;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkStatsUtils;
@@ -58,6 +56,7 @@
 import java.io.DataInput;
 import java.io.DataInputStream;
 import java.io.DataOutput;
+import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -83,9 +82,6 @@
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
-    /** Default buffer size from BufferedInputStream */
-    private static final int BUFFER_SIZE = 8192;
-
     private static final int VERSION_NETWORK_INIT = 1;
 
     private static final int VERSION_UID_INIT = 1;
@@ -439,8 +435,7 @@
 
     @Override
     public void read(InputStream in) throws IOException {
-        final FastDataInput dataIn = new FastDataInput(in, BUFFER_SIZE);
-        read(dataIn);
+        read((DataInput) new DataInputStream(in));
     }
 
     private void read(DataInput in) throws IOException {
@@ -479,9 +474,8 @@
 
     @Override
     public void write(OutputStream out) throws IOException {
-        final FastDataOutput dataOut = new FastDataOutput(out, BUFFER_SIZE);
-        write(dataOut);
-        dataOut.flush();
+        write((DataOutput) new DataOutputStream(out));
+        out.flush();
     }
 
     private void write(DataOutput out) throws IOException {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
deleted file mode 100644
index 0e9a9da..0000000
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsManagerInternal.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import android.annotation.NonNull;
-import android.net.NetworkStats;
-import android.net.NetworkTemplate;
-
-public abstract class NetworkStatsManagerInternal {
-    /** Return network layer usage total for traffic that matches template. */
-    public abstract long getNetworkTotalBytes(NetworkTemplate template, long start, long end);
-
-    /** Return network layer usage per-UID for traffic that matches template. */
-    public abstract NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end);
-
-    /** Mark given UID as being in foreground for stats purposes. */
-    public abstract void setUidForeground(int uid, boolean uidForeground);
-
-    /** Advise persistance threshold; may be overridden internally. */
-    public abstract void advisePersistThreshold(long thresholdBytes);
-
-    /** Force update of statistics. */
-    public abstract void forceUpdate();
-
-    /**
-     * Set the warning and limit to all registered custom network stats providers.
-     * Note that invocation of any interface will be sent to all providers.
-     */
-    public abstract void setStatsProviderWarningAndLimitAsync(@NonNull String iface, long warning,
-            long limit);
-}
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..e15acf3 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -44,6 +44,7 @@
 import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.net.TrafficStats.UNSUPPORTED;
 import static android.os.Trace.TRACE_TAG_NETWORK;
 import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
@@ -70,7 +71,7 @@
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
 import static com.android.net.module.util.NetworkCapabilitiesUtils.getDisplayTransport;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
@@ -89,13 +90,13 @@
 import android.database.ContentObserver;
 import android.net.DataUsageRequest;
 import android.net.INetd;
-import android.net.INetworkManagementEventObserver;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkIdentitySet;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
@@ -106,6 +107,7 @@
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
+import android.net.TetherStatsParcel;
 import android.net.TetheringManager;
 import android.net.TrafficStats;
 import android.net.UnderlyingNetworkInfo;
@@ -120,12 +122,12 @@
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -148,12 +150,12 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FileRotator;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
 import com.android.net.module.util.BestClock;
 import com.android.net.module.util.BinderUtils;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.NetworkStatsUtils;
 import com.android.net.module.util.PermissionUtils;
-import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 
 import java.io.File;
@@ -207,8 +209,15 @@
 
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
+    /**
+     * EventLog tags used when logging into the event log. Note the values must be sync with
+     * frameworks/base/services/core/java/com/android/server/EventLogTags.logtags to get correct
+     * name translation.
+      */
+    private static final int LOG_TAG_NETSTATS_MOBILE_SAMPLE = 51100;
+    private static final int LOG_TAG_NETSTATS_WIFI_SAMPLE = 51101;
+
     private final Context mContext;
-    private final INetworkManagementService mNetworkManager;
     private final NetworkStatsFactory mStatsFactory;
     private final AlarmManager mAlarmManager;
     private final Clock mClock;
@@ -223,6 +232,9 @@
     private final ContentObserver mContentObserver;
     private final ContentResolver mContentResolver;
 
+    protected INetd mNetd;
+    private final AlertObserver mAlertObserver = new AlertObserver();
+
     @VisibleForTesting
     public static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
@@ -405,20 +417,20 @@
         }
     }
 
-    public static NetworkStatsService create(Context context,
-                INetworkManagementService networkManager) {
+    /** Creates a new NetworkStatsService */
+    public static NetworkStatsService create(Context context) {
         AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         PowerManager.WakeLock wakeLock =
                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         final INetd netd = INetd.Stub.asInterface(
                 (IBinder) context.getSystemService(Context.NETD_SERVICE));
-        final NetworkStatsService service = new NetworkStatsService(context, networkManager,
+        final NetworkStatsService service = new NetworkStatsService(context,
+                INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
                 alarmManager, wakeLock, getDefaultClock(),
                 new DefaultNetworkStatsSettings(context), new NetworkStatsFactory(netd),
                 new NetworkStatsObservers(), getDefaultSystemDir(), getDefaultBaseDir(),
                 new Dependencies());
-        service.registerLocalService();
 
         return service;
     }
@@ -426,14 +438,12 @@
     // This must not be called outside of tests, even within the same package, as this constructor
     // does not register the local service. Use the create() helper above.
     @VisibleForTesting
-    NetworkStatsService(Context context, INetworkManagementService networkManager,
-            AlarmManager alarmManager, PowerManager.WakeLock wakeLock, Clock clock,
-            NetworkStatsSettings settings, NetworkStatsFactory factory,
-            NetworkStatsObservers statsObservers, File systemDir, File baseDir,
-            @NonNull Dependencies deps) {
+    NetworkStatsService(Context context, INetd netd, AlarmManager alarmManager,
+            PowerManager.WakeLock wakeLock, Clock clock, NetworkStatsSettings settings,
+            NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
+            File baseDir, @NonNull Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing Context");
-        mNetworkManager = Objects.requireNonNull(networkManager,
-                "missing INetworkManagementService");
+        mNetd = Objects.requireNonNull(netd, "missing Netd");
         mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
         mClock = Objects.requireNonNull(clock, "missing Clock");
         mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
@@ -501,9 +511,24 @@
         }
     }
 
-    private void registerLocalService() {
-        LocalServices.addService(NetworkStatsManagerInternal.class,
-                new NetworkStatsManagerInternalImpl());
+    /**
+     * Observer that watches for {@link INetdUnsolicitedEventListener} alerts.
+     */
+    @VisibleForTesting
+    public class AlertObserver extends BaseNetdUnsolicitedEventListener {
+        @Override
+        public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) {
+            PermissionUtils.enforceNetworkStackPermission(mContext);
+
+            if (LIMIT_GLOBAL_ALERT.equals(alertName)) {
+                // kick off background poll to collect network stats unless there is already
+                // such a call pending; UID stats are handled during normal polling interval.
+                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+                            mSettings.getPollDelay());
+                }
+            }
+        }
     }
 
     public void systemReady() {
@@ -551,9 +576,9 @@
         mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
 
         try {
-            mNetworkManager.registerObserver(mAlertObserver);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
+            mNetd.registerUnsolicitedEventListener(mAlertObserver);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.wtf(TAG, "Error registering event listener :", e);
         }
 
         //  schedule periodic pall alarm based on {@link NetworkStatsSettings#getPollInterval()}.
@@ -641,13 +666,13 @@
     }
 
     /**
-     * Register for a global alert that is delivered through {@link INetworkManagementEventObserver}
+     * Register for a global alert that is delivered through {@link AlertObserver}
      * or {@link NetworkStatsProviderCallback#onAlertReached()} once a threshold amount of data has
      * been transferred.
      */
     private void registerGlobalAlert() {
         try {
-            mNetworkManager.setGlobalAlert(mGlobalAlertBytes);
+            mNetd.bandwidthSetGlobalAlert(mGlobalAlertBytes);
         } catch (IllegalStateException e) {
             Log.w(TAG, "problem registering for global alert: " + e);
         } catch (RemoteException e) {
@@ -766,8 +791,18 @@
                     }
                     return stats;
                 } catch (NullPointerException e) {
-                    // TODO: Track down and fix the cause of this crash and remove this catch block.
-                    Log.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    throw e;
+                }
+            }
+
+            @Override
+            public NetworkStats getTaggedSummaryForAllUid(
+                    NetworkTemplate template, long start, long end) {
+                try {
+                    final NetworkStats tagStats = getUidTagComplete()
+                            .getSummary(template, start, end, mAccessLevel, mCallingUid);
+                    return tagStats;
+                } catch (NullPointerException e) {
                     throw e;
                 }
             }
@@ -827,7 +862,7 @@
             if (LOGD) Log.d(TAG, "Resolving plan for " + template);
             final long token = Binder.clearCallingIdentity();
             try {
-                plan = LocalServices.getService(NetworkPolicyManagerInternal.class)
+                plan = mContext.getSystemService(NetworkPolicyManager.class)
                         .getSubscriptionPlan(template);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -922,6 +957,7 @@
 
     @Override
     public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         try {
             final String[] ifacesToQuery =
                     mStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
@@ -965,7 +1001,8 @@
     }
 
     @VisibleForTesting
-    void setUidForeground(int uid, boolean uidForeground) {
+    public void setUidForeground(int uid, boolean uidForeground) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
             final int oldSet = mActiveUidCounterSet.get(uid, SET_DEFAULT);
@@ -1001,7 +1038,7 @@
 
     @Override
     public void forceUpdate() {
-        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        PermissionUtils.enforceNetworkStackPermission(mContext);
 
         final long token = Binder.clearCallingIdentity();
         try {
@@ -1011,7 +1048,9 @@
         }
     }
 
-    private void advisePersistThreshold(long thresholdBytes) {
+    /** Advise persistence threshold; may be overridden internally. */
+    public void advisePersistThreshold(long thresholdBytes) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
         // clamp threshold into safe range
         mPersistThreshold = NetworkStatsUtils.constrain(thresholdBytes,
                 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
@@ -1226,26 +1265,6 @@
     };
 
     /**
-     * Observer that watches for {@link INetworkManagementService} alerts.
-     */
-    private final INetworkManagementEventObserver mAlertObserver = new BaseNetworkObserver() {
-        @Override
-        public void limitReached(String limitName, String iface) {
-            // only someone like NMS should be calling us
-            PermissionUtils.enforceNetworkStackPermission(mContext);
-
-            if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
-                // kick off background poll to collect network stats unless there is already
-                // such a call pending; UID stats are handled during normal polling interval.
-                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
-                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
-                            mSettings.getPollDelay());
-                }
-            }
-        }
-    };
-
-    /**
      * Handle collapsed RAT type changed event.
      */
     @VisibleForTesting
@@ -1610,7 +1629,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsMobileSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_MOBILE_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1622,7 +1641,7 @@
         xtTotal = mXtRecorder.getTotalSinceBootLocked(template);
         uidTotal = mUidRecorder.getTotalSinceBootLocked(template);
 
-        EventLogTags.writeNetstatsWifiSample(
+        EventLog.writeEvent(LOG_TAG_NETSTATS_WIFI_SAMPLE,
                 devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets,
                 xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets,
                 uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets,
@@ -1668,52 +1687,19 @@
         removeUidsLocked(CollectionUtils.toIntArray(uids));
     }
 
-    private class NetworkStatsManagerInternalImpl extends NetworkStatsManagerInternal {
-        @Override
-        public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
-            try {
-                return NetworkStatsService.this.getNetworkTotalBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
+    /**
+     * Set the warning and limit to all registered custom network stats providers.
+     * Note that invocation of any interface will be sent to all providers.
+     */
+    public void setStatsProviderWarningAndLimitAsync(
+            @NonNull String iface, long warning, long limit) {
+        PermissionUtils.enforceNetworkStackPermission(mContext);
+        if (LOGV) {
+            Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
+                    + iface + "," + warning + "," + limit + ")");
         }
-
-        @Override
-        public NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
-            try {
-                return NetworkStatsService.this.getNetworkUidBytes(template, start, end);
-            } finally {
-                Trace.traceEnd(TRACE_TAG_NETWORK);
-            }
-        }
-
-        @Override
-        public void setUidForeground(int uid, boolean uidForeground) {
-            NetworkStatsService.this.setUidForeground(uid, uidForeground);
-        }
-
-        @Override
-        public void advisePersistThreshold(long thresholdBytes) {
-            NetworkStatsService.this.advisePersistThreshold(thresholdBytes);
-        }
-
-        @Override
-        public void forceUpdate() {
-            NetworkStatsService.this.forceUpdate();
-        }
-
-        @Override
-        public void setStatsProviderWarningAndLimitAsync(
-                @NonNull String iface, long warning, long limit) {
-            if (LOGV) {
-                Log.v(TAG, "setStatsProviderWarningAndLimitAsync("
-                        + iface + "," + warning + "," + limit + ")");
-            }
-            invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
-                    warning, limit));
-        }
+        invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.onSetWarningAndLimit(iface,
+                warning, limit));
     }
 
     @Override
@@ -1956,13 +1942,29 @@
      */
     // TODO: Remove this by implementing {@link NetworkStatsProvider} for non-offloaded
     //  tethering stats.
-    private NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+    private @NonNull NetworkStats getNetworkStatsTethering(int how) throws RemoteException {
+         // We only need to return per-UID stats. Per-device stats are already counted by
+        // interface counters.
+        if (how != STATS_PER_UID) {
+            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
+        }
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
         try {
-            return mNetworkManager.getNetworkStatsTethering(how);
+            final TetherStatsParcel[] tetherStatsParcels = mNetd.tetherGetStats();
+            for (TetherStatsParcel tetherStats : tetherStatsParcels) {
+                try {
+                    stats.combineValues(new NetworkStats.Entry(tetherStats.iface, UID_TETHERING,
+                            SET_DEFAULT, TAG_NONE, tetherStats.rxBytes, tetherStats.rxPackets,
+                            tetherStats.txBytes, tetherStats.txPackets, 0L));
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    throw new IllegalStateException("invalid tethering stats " + e);
+                }
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem reading network stats", e);
-            return new NetworkStats(0L, 10);
         }
+        return stats;
     }
 
     // TODO: It is copied from ConnectivityService, consider refactor these check permission
@@ -2001,10 +2003,12 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
         Objects.requireNonNull(provider, "provider is null");
         Objects.requireNonNull(tag, "tag is null");
+        final NetworkPolicyManager netPolicyManager = mContext
+                .getSystemService(NetworkPolicyManager.class);
         try {
             NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl(
                     tag, provider, mStatsProviderSem, mAlertObserver,
-                    mStatsProviderCbList);
+                    mStatsProviderCbList, netPolicyManager);
             mStatsProviderCbList.add(callback);
             Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid="
                     + getCallingUid() + "/" + getCallingPid());
@@ -2044,8 +2048,9 @@
 
         @NonNull final INetworkStatsProvider mProvider;
         @NonNull private final Semaphore mSemaphore;
-        @NonNull final INetworkManagementEventObserver mAlertObserver;
+        @NonNull final AlertObserver mAlertObserver;
         @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList;
+        @NonNull final NetworkPolicyManager mNetworkPolicyManager;
 
         @NonNull private final Object mProviderStatsLock = new Object();
 
@@ -2058,8 +2063,9 @@
         NetworkStatsProviderCallbackImpl(
                 @NonNull String tag, @NonNull INetworkStatsProvider provider,
                 @NonNull Semaphore semaphore,
-                @NonNull INetworkManagementEventObserver alertObserver,
-                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList)
+                @NonNull AlertObserver alertObserver,
+                @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList,
+                @NonNull NetworkPolicyManager networkPolicyManager)
                 throws RemoteException {
             mTag = tag;
             mProvider = provider;
@@ -2067,6 +2073,7 @@
             mSemaphore = semaphore;
             mAlertObserver = alertObserver;
             mStatsProviderCbList = cbList;
+            mNetworkPolicyManager = networkPolicyManager;
         }
 
         @NonNull
@@ -2106,15 +2113,14 @@
             // This binder object can only have been obtained by a process that holds
             // NETWORK_STATS_PROVIDER. Thus, no additional permission check is required.
             BinderUtils.withCleanCallingIdentity(() ->
-                    mAlertObserver.limitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
+                    mAlertObserver.onQuotaLimitReached(LIMIT_GLOBAL_ALERT, null /* unused */));
         }
 
         @Override
         public void notifyWarningOrLimitReached() {
             Log.d(TAG, mTag + ": notifyWarningOrLimitReached");
             BinderUtils.withCleanCallingIdentity(() ->
-                    LocalServices.getService(NetworkPolicyManagerInternal.class)
-                            .onStatsProviderWarningOrLimitReached(mTag));
+                    mNetworkPolicyManager.onStatsProviderWarningOrLimitReached());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a2c2dbd..3951680 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,6 +39,8 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.TrafficStats.UID_TETHERING;
 
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
+
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
@@ -133,12 +135,6 @@
 
     private static final int MAX_UID_RANGES_PER_COMMAND = 10;
 
-    /**
-     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
-     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
-     */
-    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
-
     static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
 
     static final boolean MODIFY_OPERATION_ADD = true;
diff --git a/services/core/java/com/android/server/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java
new file mode 100644
index 0000000..2dff72f
--- /dev/null
+++ b/services/core/java/com/android/server/SmartStorageMaintIdler.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+public class SmartStorageMaintIdler extends JobService {
+    private static final String TAG = "SmartStorageMaintIdler";
+
+    private static final ComponentName SMART_STORAGE_MAINT_SERVICE =
+            new ComponentName("android", SmartStorageMaintIdler.class.getName());
+
+    private static final int SMART_MAINT_JOB_ID = 2808;
+
+    private boolean mStarted;
+    private JobParameters mJobParams;
+    private final Runnable mFinishCallback = new Runnable() {
+        @Override
+        public void run() {
+            Slog.i(TAG, "Got smart storage maintenance service completion callback");
+            if (mStarted) {
+                jobFinished(mJobParams, false);
+                mStarted = false;
+            }
+            // ... and try again in a next period
+            scheduleSmartIdlePass(SmartStorageMaintIdler.this,
+                StorageManagerService.SMART_IDLE_MAINT_PERIOD);
+        }
+    };
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        mJobParams = params;
+        StorageManagerService ms = StorageManagerService.sSelf;
+        if (ms != null) {
+            mStarted = true;
+            ms.runSmartIdleMaint(mFinishCallback);
+        }
+        return ms != null;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        mStarted = false;
+        return false;
+    }
+
+    /**
+     * Schedule the smart storage idle maintenance job
+     */
+    public static void scheduleSmartIdlePass(Context context, int nHours) {
+        StorageManagerService ms = StorageManagerService.sSelf;
+        if ((ms == null) || ms.isPassedLifetimeThresh()) {
+            return;
+        }
+
+        JobScheduler tm = context.getSystemService(JobScheduler.class);
+
+        long nextScheduleTime = TimeUnit.HOURS.toMillis(nHours);
+
+        JobInfo.Builder builder = new JobInfo.Builder(SMART_MAINT_JOB_ID,
+            SMART_STORAGE_MAINT_SERVICE);
+
+        builder.setMinimumLatency(nextScheduleTime);
+        tm.schedule(builder.build());
+    }
+}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 2d6170b..53c8635 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -79,6 +79,7 @@
 import android.content.res.ObbInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.os.Environment;
@@ -158,6 +159,8 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver;
 
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
@@ -339,7 +342,44 @@
 
     @Nullable public static String sMediaStoreAuthorityProcessName;
 
+    // Run period in hour for smart idle maintenance
+    static final int SMART_IDLE_MAINT_PERIOD = 1;
+
     private final AtomicFile mSettingsFile;
+    private final AtomicFile mHourlyWriteFile;
+
+    private static final int MAX_HOURLY_WRITE_RECORDS = 72;
+
+    /**
+     * Default config values for smart idle maintenance
+     * Actual values will be controlled by DeviceConfig
+     */
+    // Decide whether smart idle maintenance is enabled or not
+    private static final boolean DEFAULT_SMART_IDLE_MAINT_ENABLED = false;
+    // Storage lifetime percentage threshold to decide to turn off the feature
+    private static final int DEFAULT_LIFETIME_PERCENT_THRESHOLD = 70;
+    // Minimum required number of dirty + free segments to trigger GC
+    private static final int DEFAULT_MIN_SEGMENTS_THRESHOLD = 512;
+    // Determine how much portion of current dirty segments will be GCed
+    private static final float DEFAULT_DIRTY_RECLAIM_RATE = 0.5F;
+    // Multiplier to amplify the target segment number for GC
+    private static final float DEFAULT_SEGMENT_RECLAIM_WEIGHT = 1.0F;
+    // Low battery level threshold to decide to turn off the feature
+    private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
+    // Decide whether charging is required to turn on the feature
+    private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+
+    private volatile int mLifetimePercentThreshold;
+    private volatile int mMinSegmentsThreshold;
+    private volatile float mDirtyReclaimRate;
+    private volatile float mSegmentReclaimWeight;
+    private volatile float mLowBatteryLevel;
+    private volatile boolean mChargingRequired;
+    private volatile boolean mNeedGC;
+
+    private volatile boolean mPassedLifetimeThresh;
+    // Tracking storage hourly write amounts
+    private volatile int[] mStorageHourlyWrites;
 
     /**
      * <em>Never</em> hold the lock while performing downcalls into vold, since
@@ -901,6 +941,10 @@
     }
 
     private void handleSystemReady() {
+        if (prepareSmartIdleMaint()) {
+            SmartStorageMaintIdler.scheduleSmartIdlePass(mContext, SMART_IDLE_MAINT_PERIOD);
+        }
+
         // Start scheduling nominally-daily fstrim operations
         MountServiceIdler.scheduleIdlePass(mContext);
 
@@ -1907,6 +1951,10 @@
 
         mSettingsFile = new AtomicFile(
                 new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings");
+        mHourlyWriteFile = new AtomicFile(
+                new File(Environment.getDataSystemDirectory(), "storage-hourly-writes"));
+
+        mStorageHourlyWrites = new int[MAX_HOURLY_WRITE_RECORDS];
 
         synchronized (mLock) {
             readSettingsLocked();
@@ -2572,7 +2620,7 @@
             // fstrim time is still updated. If file based checkpoints are used, we run
             // idle maintenance (GC + fstrim) regardless of checkpoint status.
             if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
-                mVold.runIdleMaint(new IVoldTaskListener.Stub() {
+                mVold.runIdleMaint(mNeedGC, new IVoldTaskListener.Stub() {
                     @Override
                     public void onStatus(int status, PersistableBundle extras) {
                         // Not currently used
@@ -2623,6 +2671,176 @@
         abortIdleMaint(null);
     }
 
+    private boolean prepareSmartIdleMaint() {
+        /**
+         * We can choose whether going with a new storage smart idle maintenance job
+         * or falling back to the traditional way using DeviceConfig
+         */
+        boolean smartIdleMaintEnabled = DeviceConfig.getBoolean(
+            DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+            "smart_idle_maint_enabled",
+            DEFAULT_SMART_IDLE_MAINT_ENABLED);
+        if (smartIdleMaintEnabled) {
+            mLifetimePercentThreshold = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "lifetime_threshold", DEFAULT_LIFETIME_PERCENT_THRESHOLD);
+            mMinSegmentsThreshold = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "min_segments_threshold", DEFAULT_MIN_SEGMENTS_THRESHOLD);
+            mDirtyReclaimRate = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "dirty_reclaim_rate", DEFAULT_DIRTY_RECLAIM_RATE);
+            mSegmentReclaimWeight = DeviceConfig.getFloat(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "segment_reclaim_weight", DEFAULT_SEGMENT_RECLAIM_WEIGHT);
+            mLowBatteryLevel = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
+            mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "charging_required", DEFAULT_CHARGING_REQUIRED);
+
+            // If we use the smart idle maintenance, we need to turn off GC in the traditional idle
+            // maintenance to avoid the conflict
+            mNeedGC = false;
+
+            loadStorageHourlyWrites();
+            try {
+                mVold.refreshLatestWrite();
+            } catch (Exception e) {
+                Slog.wtf(TAG, e);
+            }
+            refreshLifetimeConstraint();
+        }
+        return smartIdleMaintEnabled;
+    }
+
+    // Return whether storage lifetime exceeds the threshold
+    public boolean isPassedLifetimeThresh() {
+        return mPassedLifetimeThresh;
+    }
+
+    private void loadStorageHourlyWrites() {
+        FileInputStream fis = null;
+
+        try {
+            fis = mHourlyWriteFile.openRead();
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            mStorageHourlyWrites = (int[])ois.readObject();
+        } catch (FileNotFoundException e) {
+            // Missing data is okay, probably first boot
+        } catch (Exception e) {
+            Slog.wtf(TAG, "Failed reading write records", e);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
+    }
+
+    private int getAverageHourlyWrite() {
+        return Arrays.stream(mStorageHourlyWrites).sum() / MAX_HOURLY_WRITE_RECORDS;
+    }
+
+    private void updateStorageHourlyWrites(int latestWrite) {
+        FileOutputStream fos = null;
+
+        System.arraycopy(mStorageHourlyWrites,0, mStorageHourlyWrites, 1,
+                     MAX_HOURLY_WRITE_RECORDS - 1);
+        mStorageHourlyWrites[0] = latestWrite;
+        try {
+            fos = mHourlyWriteFile.startWrite();
+            ObjectOutputStream oos = new ObjectOutputStream(fos);
+            oos.writeObject(mStorageHourlyWrites);
+            mHourlyWriteFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                mHourlyWriteFile.failWrite(fos);
+            }
+        }
+    }
+
+    private boolean checkChargeStatus() {
+        IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+        Intent batteryStatus = mContext.registerReceiver(null, ifilter);
+
+        if (mChargingRequired) {
+            int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
+            if (status != BatteryManager.BATTERY_STATUS_CHARGING &&
+                status != BatteryManager.BATTERY_STATUS_FULL) {
+                Slog.w(TAG, "Battery is not being charged");
+                return false;
+            }
+        }
+
+        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+        float chargePercent = level * 100f / (float)scale;
+
+        if (chargePercent < mLowBatteryLevel) {
+            Slog.w(TAG, "Battery level is " + chargePercent + ", which is lower than threshold: " +
+                        mLowBatteryLevel);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean refreshLifetimeConstraint() {
+        int storageLifeTime = 0;
+
+        try {
+            storageLifeTime = mVold.getStorageLifeTime();
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+            return false;
+        }
+
+        if (storageLifeTime == -1) {
+            Slog.w(TAG, "Failed to get storage lifetime");
+            return false;
+        } else if (storageLifeTime > mLifetimePercentThreshold) {
+            Slog.w(TAG, "Ended smart idle maintenance, because of lifetime(" + storageLifeTime +
+                        ")" + ", lifetime threshold(" + mLifetimePercentThreshold + ")");
+            mPassedLifetimeThresh = true;
+            return false;
+        }
+        Slog.i(TAG, "Storage lifetime: " + storageLifeTime);
+        return true;
+    }
+
+    void runSmartIdleMaint(Runnable callback) {
+        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+
+        try {
+            // Block based checkpoint process runs fstrim. So, if checkpoint is in progress
+            // (first boot after OTA), We skip the smart idle maintenance
+            if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
+                if (!refreshLifetimeConstraint() || !checkChargeStatus()) {
+                    return;
+                }
+
+                int latestHourlyWrite = mVold.getWriteAmount();
+                if (latestHourlyWrite == -1) {
+                    Slog.w(TAG, "Failed to get storage hourly write");
+                    return;
+                }
+
+                updateStorageHourlyWrites(latestHourlyWrite);
+                int avgHourlyWrite = getAverageHourlyWrite();
+
+                Slog.i(TAG, "Set smart idle maintenance: " + "latest hourly write: " +
+                            latestHourlyWrite + ", average hourly write: " + avgHourlyWrite +
+                            ", min segment threshold: " + mMinSegmentsThreshold +
+                            ", dirty reclaim rate: " + mDirtyReclaimRate +
+                            ", segment reclaim weight:" + mSegmentReclaimWeight);
+                mVold.setGCUrgentPace(avgHourlyWrite, mMinSegmentsThreshold, mDirtyReclaimRate,
+                                      mSegmentReclaimWeight);
+            } else {
+                Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
+            }
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+        } finally {
+            if (callback != null) {
+                callback.run();
+            }
+        }
+    }
+
     @Override
     public void setDebugFlags(int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 7ba032f..c6bca19 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;
@@ -702,8 +703,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);
             }
@@ -712,8 +715,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 9a349ef..d75ddba 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;
@@ -2025,8 +2026,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);
             });
         }
     }
@@ -2063,9 +2067,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/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0a114b9..fba8c05 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1088,6 +1088,12 @@
     private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
             String eventSource) {
         if (device != AudioSystem.DEVICE_NONE) {
+
+            /* Audio Policy sees Le Audio similar to A2DP. Let's make sure
+             * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
+             */
+            mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
+
             AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
                     address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
             mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 093ecd5..3d5abfe 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -535,9 +535,6 @@
         mEmulatorClipboardMonitor.accept(clip);
 
         final int userId = UserHandle.getUserId(uid);
-        if (clip != null) {
-            startClassificationLocked(clip, userId);
-        }
 
         // Update this user
         setPrimaryClipInternalLocked(getClipboardLocked(userId), clip, uid, sourcePackage);
@@ -593,6 +590,17 @@
     @GuardedBy("mLock")
     private void setPrimaryClipInternalLocked(PerUserClipboard clipboard, @Nullable ClipData clip,
             int uid, @Nullable String sourcePackage) {
+        final int userId = UserHandle.getUserId(uid);
+        if (clip != null) {
+            startClassificationLocked(clip, userId);
+        }
+
+        setPrimaryClipInternalNoClassifyLocked(clipboard, clip, uid, sourcePackage);
+    }
+
+    @GuardedBy("mLock")
+    private void setPrimaryClipInternalNoClassifyLocked(PerUserClipboard clipboard,
+            @Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
         revokeUris(clipboard);
         clipboard.activePermissionOwners.clear();
         if (clip == null && clipboard.primaryClip == null) {
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/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 03a63b9..8ef42ff 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,11 +16,8 @@
 
 package com.android.server.net;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Network;
-import android.net.NetworkTemplate;
-import android.net.netstats.provider.NetworkStatsProvider;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.telephony.SubscriptionPlan;
 
@@ -56,11 +53,6 @@
      */
     public abstract SubscriptionPlan getSubscriptionPlan(Network network);
 
-    /**
-     * Return the active {@link SubscriptionPlan} for the given template.
-     */
-    public abstract SubscriptionPlan getSubscriptionPlan(NetworkTemplate template);
-
     public static final int QUOTA_TYPE_JOBS = 1;
     public static final int QUOTA_TYPE_MULTIPATH = 2;
 
@@ -99,13 +91,4 @@
      */
     public abstract void setMeteredRestrictedPackagesAsync(
             Set<String> packageNames, int userId);
-
-    /**
-     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
-     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
-     *  {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
-     *
-     * @param tag the human readable identifier of the custom network stats provider.
-     */
-    public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2ca057d..e1cbdb7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -130,7 +130,7 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
+import static com.android.net.module.util.NetworkStatsUtils.LIMIT_GLOBAL_ALERT;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -151,6 +151,8 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -181,7 +183,6 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.TrafficStats;
@@ -443,7 +444,7 @@
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
-    private NetworkStatsManagerInternal mNetworkStats;
+    private NetworkStatsManager mNetworkStats;
     private final INetworkManagementService mNetworkManager;
     private UsageStatsManagerInternal mUsageStats;
     private AppStandbyInternal mAppStandby;
@@ -455,6 +456,8 @@
     private ConnectivityManager mConnManager;
     private PowerManagerInternal mPowerManagerInternal;
     private PowerWhitelistManager mPowerWhitelistManager;
+    @NonNull
+    private final Dependencies mDeps;
 
     /** Current cached value of the current Battery Saver mode's setting for restrict background. */
     @GuardedBy("mUidRulesFirstLock")
@@ -706,7 +709,7 @@
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement) {
         this(context, activityManager, networkManagement, AppGlobals.getPackageManager(),
-                getDefaultClock(), getDefaultSystemDir(), false);
+                getDefaultClock(), getDefaultSystemDir(), false, new Dependencies(context));
     }
 
     private static @NonNull File getDefaultSystemDir() {
@@ -718,9 +721,59 @@
                 Clock.systemUTC());
     }
 
+    static class Dependencies {
+        final Context mContext;
+        final NetworkStatsManager mNetworkStatsManager;
+        Dependencies(Context context) {
+            mContext = context;
+            mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+            // Query stats from NetworkStatsService will trigger a poll by default.
+            // But since NPMS listens stats updated event, and will query stats
+            // after the event. A polling -> updated -> query -> polling loop will be introduced
+            // if polls on open. Hence, while NPMS manages it's poll requests explicitly, set
+            // flag to false to prevent a polling loop.
+            mNetworkStatsManager.setPollOnOpen(false);
+        }
+
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkTotalBytes");
+            try {
+                final NetworkStats.Bucket ret = mNetworkStatsManager
+                        .querySummaryForDevice(template, start, end);
+                return ret.getRxBytes() + ret.getTxBytes();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+                return 0;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+        }
+
+        @NonNull
+        List<NetworkStats.Bucket> getNetworkUidBytes(
+                @NonNull NetworkTemplate template, long start, long end) {
+            Trace.traceBegin(TRACE_TAG_NETWORK, "getNetworkUidBytes");
+            final List<NetworkStats.Bucket> buckets = new ArrayList<>();
+            try {
+                final NetworkStats stats = mNetworkStatsManager.querySummary(template, start, end);
+                while (stats.hasNextBucket()) {
+                    final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                    stats.getNextBucket(bucket);
+                    buckets.add(bucket);
+                }
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failed to read network stats: " + e);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_NETWORK);
+            }
+            return buckets;
+        }
+    }
+
+    @VisibleForTesting
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
-            File systemDir, boolean suppressDefaultPolicy) {
+            File systemDir, boolean suppressDefaultPolicy, Dependencies deps) {
         mContext = Objects.requireNonNull(context, "missing context");
         mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
         mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
@@ -741,10 +794,12 @@
         mUidEventHandler = new Handler(mUidEventThread.getLooper(), mUidEventHandlerCallback);
 
         mSuppressDefaultPolicy = suppressDefaultPolicy;
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
 
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"), "net-policy");
 
         mAppOps = context.getSystemService(AppOpsManager.class);
+        mNetworkStats = context.getSystemService(NetworkStatsManager.class);
         mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
         // Expose private service for system components to use.
         LocalServices.addService(NetworkPolicyManagerInternal.class,
@@ -844,7 +899,6 @@
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
             mAppStandby = LocalServices.getService(AppStandbyInternal.class);
-            mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class);
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -1167,21 +1221,34 @@
     };
 
     /**
-     * Receiver that watches for {@link INetworkStatsService} updates, which we
+     * Receiver that watches for {@link NetworkStatsManager} updates, which we
      * use to check against {@link NetworkPolicy#warningBytes}.
      */
-    final private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() {
+    private final NetworkStatsBroadcastReceiver mStatsReceiver =
+            new NetworkStatsBroadcastReceiver();
+    private class NetworkStatsBroadcastReceiver extends BroadcastReceiver {
+        private boolean mIsAnyIntentReceived = false;
         @Override
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified
             // READ_NETWORK_USAGE_HISTORY permission above.
 
+            mIsAnyIntentReceived = true;
+
             synchronized (mNetworkPoliciesSecondLock) {
                 updateNetworkRulesNL();
                 updateNetworkEnabledNL();
                 updateNotificationsNL();
             }
         }
+
+        /**
+         * Return whether any {@code ACTION_NETWORK_STATS_UPDATED} intent is received.
+         * Used to determine if NetworkStatsService is ready.
+         */
+        public boolean isAnyIntentReceived() {
+            return mIsAnyIntentReceived;
+        }
     };
 
     /**
@@ -1405,15 +1472,17 @@
         long maxBytes = 0;
         int maxUid = 0;
 
-        final NetworkStats stats = getNetworkUidBytes(template, start, end);
-        NetworkStats.Entry entry = null;
-        for (int i = 0; i < stats.size(); i++) {
-            entry = stats.getValues(i, entry);
-            final long bytes = entry.rxBytes + entry.txBytes;
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return null;
+
+        final List<NetworkStats.Bucket> stats = mDeps.getNetworkUidBytes(template, start, end);
+        for (final NetworkStats.Bucket entry : stats) {
+            final long bytes = entry.getRxBytes() + entry.getTxBytes();
             totalBytes += bytes;
             if (bytes > maxBytes) {
                 maxBytes = bytes;
-                maxUid = entry.uid;
+                maxUid = entry.getUid();
             }
         }
 
@@ -3383,6 +3452,35 @@
         return result;
     }
 
+    /**
+     * Get subscription plan for the given networkTemplate.
+     *
+     * @param template the networkTemplate to get the subscription plan for.
+     */
+    @Override
+    public SubscriptionPlan getSubscriptionPlan(@NonNull NetworkTemplate template) {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        synchronized (mNetworkPoliciesSecondLock) {
+            final int subId = findRelevantSubIdNL(template);
+            return getPrimarySubscriptionPlanLocked(subId);
+        }
+    }
+
+    /**
+     * Notifies that the specified {@link NetworkStatsProvider} has reached its quota
+     * which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
+     * {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
+     */
+    @Override
+    public void onStatsProviderWarningOrLimitReached() {
+        enforceAnyPermissionOf(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+        // This API may be called before the system is ready.
+        synchronized (mNetworkPoliciesSecondLock) {
+            if (!mSystemReady) return;
+        }
+        mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -5369,25 +5467,10 @@
 
     @Deprecated
     private long getTotalBytes(NetworkTemplate template, long start, long end) {
-        return getNetworkTotalBytes(template, start, end);
-    }
-
-    private long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkTotalBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return 0;
-        }
-    }
-
-    private NetworkStats getNetworkUidBytes(NetworkTemplate template, long start, long end) {
-        try {
-            return mNetworkStats.getNetworkUidBytes(template, start, end);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failed to read network stats: " + e);
-            return new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        }
+        // Skip if not ready. NetworkStatsService will block public API calls until it is
+        // ready. To prevent NPMS be blocked on that, skip and fail fast instead.
+        if (!mStatsReceiver.isAnyIntentReceived()) return 0;
+        return mDeps.getNetworkTotalBytes(template, start, end);
     }
 
     private boolean isBandwidthControlEnabled() {
@@ -5607,14 +5690,6 @@
         }
 
         @Override
-        public SubscriptionPlan getSubscriptionPlan(NetworkTemplate template) {
-            synchronized (mNetworkPoliciesSecondLock) {
-                final int subId = findRelevantSubIdNL(template);
-                return getPrimarySubscriptionPlanLocked(subId);
-            }
-        }
-
-        @Override
         public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
             final long quotaBytes;
             synchronized (mNetworkPoliciesSecondLock) {
@@ -5656,12 +5731,6 @@
             mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
                     userId, 0, packageNames).sendToTarget();
         }
-
-        @Override
-        public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
-            Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
-            mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
-        }
     }
 
     private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 0f37450..e7d05b6 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -527,12 +527,13 @@
                     callback.onFailure();
                     return;
                 }
-                textClassifierServiceConsumer.accept(serviceState.mService);
+                consumeServiceNoExceptLocked(textClassifierServiceConsumer, serviceState.mService);
             } else {
                 serviceState.mPendingRequests.add(
                         new PendingRequest(
                                 methodName,
-                                () -> textClassifierServiceConsumer.accept(serviceState.mService),
+                                () -> consumeServiceNoExceptLocked(
+                                        textClassifierServiceConsumer, serviceState.mService),
                                 callback::onFailure, callback.asBinder(),
                                 this,
                                 serviceState,
@@ -541,6 +542,16 @@
         }
     }
 
+    private static void consumeServiceNoExceptLocked(
+            @NonNull ThrowingConsumer<ITextClassifierService> textClassifierServiceConsumer,
+            @Nullable ITextClassifierService service) {
+        try {
+            textClassifierServiceConsumer.accept(service);
+        } catch (RuntimeException | Error e) {
+            Slog.e(LOG_TAG, "Exception when consume textClassifierService: " + e);
+        }
+    }
+
     private static ITextClassifierCallback wrap(ITextClassifierCallback orig) {
         return new CallbackWrapper(orig);
     }
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 6db25b7..c96c1ee 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -22,8 +22,6 @@
 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;
 
 import static com.android.server.VcnManagementService.LOCAL_LOG;
 
@@ -47,6 +45,7 @@
 import com.android.server.vcn.VcnContext;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /** @hide */
@@ -121,9 +120,10 @@
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
-        // TODO: Check Network Quality reported by metric monitors/probers.
-
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
+        final boolean isSelectedUnderlyingNetwork =
+                currentlySelected != null
+                        && Objects.equals(currentlySelected.network, networkRecord.network);
 
         final int meteredMatch = networkPriority.getMetered();
         final boolean isMetered = !caps.hasCapability(NET_CAPABILITY_NOT_METERED);
@@ -132,6 +132,23 @@
             return false;
         }
 
+        // Fails bandwidth requirements if either (a) less than exit threshold, or (b), not
+        // selected, but less than entry threshold
+        if (caps.getLinkUpstreamBandwidthKbps() < networkPriority.getMinExitUpstreamBandwidthKbps()
+                || (caps.getLinkUpstreamBandwidthKbps()
+                                < networkPriority.getMinEntryUpstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
+            return false;
+        }
+
+        if (caps.getLinkDownstreamBandwidthKbps()
+                        < networkPriority.getMinExitDownstreamBandwidthKbps()
+                || (caps.getLinkDownstreamBandwidthKbps()
+                                < networkPriority.getMinEntryDownstreamBandwidthKbps()
+                        && !isSelectedUnderlyingNetwork)) {
+            return false;
+        }
+
         if (vcnContext.isInTestMode() && caps.hasTransport(TRANSPORT_TEST)) {
             return true;
         }
@@ -172,8 +189,7 @@
         }
 
         // TODO: Move the Network Quality check to the network metric monitor framework.
-        if (networkPriority.getNetworkQuality()
-                > getWifiQuality(networkRecord, currentlySelected, carrierConfig)) {
+        if (!isWifiRssiAcceptable(networkRecord, currentlySelected, carrierConfig)) {
             return false;
         }
 
@@ -185,7 +201,7 @@
         return true;
     }
 
-    private static int getWifiQuality(
+    private static boolean isWifiRssiAcceptable(
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
             PersistableBundle carrierConfig) {
@@ -196,14 +212,14 @@
 
         if (isSelectedNetwork
                 && caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
         if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
-            return NETWORK_QUALITY_OK;
+            return true;
         }
 
-        return NETWORK_QUALITY_ANY;
+        return false;
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0792d9b..df98390 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -231,6 +231,7 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivitySettingsManager;
 import android.net.IIpConnectivityMetrics;
+import android.net.ProfileNetworkPreference;
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.net.VpnManager;
@@ -17538,10 +17539,14 @@
         }
         int networkPreference = preferentialNetworkServiceEnabled
                 ? PROFILE_NETWORK_PREFERENCE_ENTERPRISE : PROFILE_NETWORK_PREFERENCE_DEFAULT;
+        ProfileNetworkPreference.Builder preferenceBuilder =
+                new ProfileNetworkPreference.Builder();
+        preferenceBuilder.setPreference(networkPreference);
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceBuilder.build());
         mInjector.binderWithCleanCallingIdentity(() ->
-                mInjector.getConnectivityManager().setProfileNetworkPreference(
-                        UserHandle.of(userId),
-                        networkPreference,
+                mInjector.getConnectivityManager().setProfileNetworkPreferences(
+                        UserHandle.of(userId), preferences,
                         null /* executor */, null /* listener */));
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2aee778..2303495 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1837,7 +1837,7 @@
 
             t.traceBegin("StartNetworkStatsService");
             try {
-                networkStats = NetworkStatsService.create(context, networkManagement);
+                networkStats = NetworkStatsService.create(context);
                 ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
             } catch (Throwable e) {
                 reportWtf("starting NetworkStats Service", e);
diff --git a/services/midi/OWNERS b/services/midi/OWNERS
new file mode 100644
index 0000000..f4d51f9
--- /dev/null
+++ b/services/midi/OWNERS
@@ -0,0 +1 @@
+philburk@google.com
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index bcb2cf8..187b012 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -93,6 +93,7 @@
     <uses-permission android:name="android.permission.CONTROL_DEVICE_STATE"/>
     <uses-permission android:name="android.permission.READ_PROJECTION_STATE"/>
     <uses-permission android:name="android.permission.KILL_UID"/>
+    <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
     <uses-permission
         android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 3ac30d02..a63aa6a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -37,6 +37,8 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
 import static android.app.admin.PasswordMetrics.computeForPasswordOrPin;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT;
+import static android.net.ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE;
 import static android.net.InetAddresses.parseNumericAddress;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
@@ -99,7 +101,7 @@
 import android.content.pm.UserInfo;
 import android.graphics.Color;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
+import android.net.ProfileNetworkPreference;
 import android.net.Uri;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
@@ -4044,12 +4046,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStartUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                anyInt(),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                null, null);
     }
 
     @Test
@@ -4061,12 +4066,15 @@
         mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         dpms.handleStopUser(managedProfileUserId);
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
     }
 
     @Test
@@ -4084,21 +4092,29 @@
 
         dpm.setPreferentialNetworkServiceEnabled(false);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isFalse();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT)
+                        .build();
+        List<ProfileNetworkPreference> preferences = new ArrayList<>();
+        preferences.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences,
+                        null, null);
 
         dpm.setPreferentialNetworkServiceEnabled(true);
         assertThat(dpm.isPreferentialNetworkServiceEnabled()).isTrue();
-        verify(getServices().connectivityManager, times(1)).setProfileNetworkPreference(
-                eq(UserHandle.of(managedProfileUserId)),
-                eq(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE),
-                any(),
-                any()
-        );
+
+        ProfileNetworkPreference preferenceDetails2 =
+                new ProfileNetworkPreference.Builder()
+                        .setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE)
+                        .build();
+        List<ProfileNetworkPreference> preferences2 = new ArrayList<>();
+        preferences2.add(preferenceDetails);
+        verify(getServices().connectivityManager, times(1))
+                .setProfileNetworkPreferences(UserHandle.of(managedProfileUserId), preferences2,
+                        null, null);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index b811e28..9a6f61e 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -49,12 +49,8 @@
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
-import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
-import static android.net.NetworkStats.SET_ALL;
-import static android.net.NetworkStats.TAG_ALL;
-import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkTemplate.buildTemplateCarrierMetered;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
@@ -75,6 +71,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -108,6 +105,8 @@
 import android.app.IUidObserver;
 import android.app.Notification;
 import android.app.NotificationManager;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -125,8 +124,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicy;
 import android.net.NetworkStateSnapshot;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiInfo;
@@ -138,7 +135,6 @@
 import android.os.PowerSaveState;
 import android.os.RemoteException;
 import android.os.SimpleClock;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
@@ -263,12 +259,13 @@
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
     private @Mock UserManager mUserManager;
+    private @Mock NetworkStatsManager mStatsManager;
+    private TestDependencies mDeps;
 
     private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
 
     private ActivityManagerInternal mActivityManagerInternal;
-    private NetworkStatsManagerInternal mStatsService;
 
     private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
@@ -335,8 +332,47 @@
                 .setBatterySaverEnabled(false).build();
         final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class);
         when(pmInternal.getLowPowerState(anyInt())).thenReturn(state);
+    }
 
-        mStatsService = addLocalServiceMock(NetworkStatsManagerInternal.class);
+    private class TestDependencies extends NetworkPolicyManagerService.Dependencies {
+        private final SparseArray<NetworkStats.Bucket> mMockedStats = new SparseArray<>();
+
+        TestDependencies(Context context) {
+            super(context);
+        }
+
+        @Override
+        long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
+            int total = 0;
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                NetworkStats.Bucket bucket = mMockedStats.valueAt(i);
+                total += bucket.getRxBytes() + bucket.getTxBytes();
+            }
+            return total;
+        }
+
+        @Override
+        List<NetworkStats.Bucket> getNetworkUidBytes(NetworkTemplate template, long start,
+                long end) {
+            final List<NetworkStats.Bucket> ret = new ArrayList<>();
+            for (int i = 0; i < mMockedStats.size(); i++) {
+                ret.add(mMockedStats.valueAt(i));
+            }
+            return ret;
+        }
+
+        private void setMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mock(NetworkStats.Bucket.class);
+            when(bucket.getUid()).thenReturn(uid);
+            when(bucket.getRxBytes()).thenReturn(rxBytes);
+            when(bucket.getTxBytes()).thenReturn(txBytes);
+            mMockedStats.set(uid, bucket);
+        }
+
+        private void increaseMockedTotalBytes(int uid, long rxBytes, long txBytes) {
+            final NetworkStats.Bucket bucket = mMockedStats.get(uid);
+            setMockedTotalBytes(uid, bucket.getRxBytes() + rxBytes, bucket.getTxBytes() + txBytes);
+        }
     }
 
     @Before
@@ -376,6 +412,8 @@
                         return mConnManager;
                     case Context.USER_SERVICE:
                         return mUserManager;
+                    case Context.NETWORK_STATS_SERVICE:
+                        return mStatsManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -400,8 +438,9 @@
         }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
+        mDeps = new TestDependencies(mServiceContext);
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager,
-                mNetworkManager, mIpm, mClock, mPolicyDir, true);
+                mNetworkManager, mIpm, mClock, mPolicyDir, true, mDeps);
         mService.bindConnectivityManager();
         mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
@@ -456,6 +495,9 @@
         verify(mNetworkManager).registerObserver(networkObserver.capture());
         mNetworkObserver = networkObserver.getValue();
 
+        // Simulate NetworkStatsService broadcast stats updated to signal its readiness.
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_UPDATED));
+
         NetworkPolicy defaultPolicy = mService.buildDefaultCarrierPolicy(0, "");
         mDefaultWarningBytes = defaultPolicy.warningBytes;
         mDefaultLimitBytes = defaultPolicy.limitBytes;
@@ -479,7 +521,6 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.removeServiceForTest(AppStandbyInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
-        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
     }
 
     @After
@@ -1108,10 +1149,7 @@
         when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
 
         // pretend that 512 bytes total have happened
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
-                .thenReturn(stats.getTotalBytes());
+        mDeps.setMockedTotalBytes(UID_A, 256L, 256L);
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
         setNetworkPolicies(new NetworkPolicy(
@@ -1124,26 +1162,6 @@
 
     @Test
     public void testNotificationWarningLimitSnooze() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1161,9 +1179,7 @@
 
         // Normal usage means no notification
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1178,9 +1194,7 @@
 
         // Push over warning
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1196,9 +1210,7 @@
 
         // Push over warning, but with a config that isn't from an identified carrier
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1799), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1215,9 +1227,7 @@
 
         // Push over limit
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(1810), 0);
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
             TelephonyManager tmSub = expectMobileDefaults();
@@ -1248,26 +1258,6 @@
 
     @Test
     public void testNotificationRapid() throws Exception {
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1285,9 +1275,7 @@
 
         // Using 20% data in 20% time is normal
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1297,16 +1285,9 @@
         // Using 80% data in 20% time is alarming; but spread equally among
         // three UIDs means we get generic alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_C, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, DataUnit.MEGABYTES.toBytes(480), 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1325,14 +1306,9 @@
         // Using 80% data in 20% time is alarming; but mostly done by one UID
         // means we get specific alert
         {
-            history.clear();
-            history.recordData(start, end,
-                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
-            stats.clear();
-            stats.insertEntry(IFACE_ALL, UID_A, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(960), 0, 0, 0, 0);
-            stats.insertEntry(IFACE_ALL, UID_B, SET_ALL, TAG_ALL,
-                    DataUnit.MEGABYTES.toBytes(480), 0, 0, 0, 0);
+            mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(960), 0);
+            mDeps.setMockedTotalBytes(UID_B, DataUnit.MEGABYTES.toBytes(480), 0);
+            mDeps.setMockedTotalBytes(UID_C, 0, 0);
 
             reset(mNotifManager);
             mService.updateNetworks();
@@ -1362,13 +1338,10 @@
 
         // bring up wifi network with metered policy
         snapshots = List.of(buildWifi());
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 0L, 0L, 0L, 0L);
+        mDeps.setMockedTotalBytes(UID_A, 0L, 0L);
 
         {
             when(mConnManager.getAllNetworkStateSnapshots()).thenReturn(snapshots);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(
@@ -1647,18 +1620,6 @@
         final NetworkPolicyManagerInternal internal = LocalServices
                 .getService(NetworkPolicyManagerInternal.class);
 
-        // Create a place to store fake usage
-        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(invocation -> {
-                    final NetworkStatsHistory.Entry entry = history.getValues(
-                            invocation.getArgument(1), invocation.getArgument(2), null);
-                    return entry.rxBytes + entry.txBytes;
-                });
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
@@ -1669,9 +1630,7 @@
         setCurrentTimeMillis(end);
 
         // Get some data usage in place
-        history.clear();
-        history.recordData(start, end,
-                new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+        mDeps.setMockedTotalBytes(UID_A, DataUnit.MEGABYTES.toBytes(360), 0);
 
         // No data plan
         {
@@ -1786,22 +1745,11 @@
                 true);
     }
 
-    private void increaseMockedTotalBytes(NetworkStats stats, long rxBytes, long txBytes) {
-        stats.insertEntry(TEST_IFACE, UID_A, SET_ALL, TAG_NONE,
-                rxBytes, 1, txBytes, 1, 0);
-        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats.getTotalBytes());
-        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenReturn(stats);
-    }
-
     private void triggerOnStatsProviderWarningOrLimitReached() throws InterruptedException {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
-        npmi.onStatsProviderWarningOrLimitReached("TEST");
+        mService.onStatsProviderWarningOrLimitReached();
         // Wait for processing of MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED.
         postMsgAndWaitForCompletion();
-        verify(mStatsService).forceUpdate();
+        verify(mStatsManager).forceUpdate();
         // Wait for processing of MSG_*_INTERFACE_QUOTAS.
         postMsgAndWaitForCompletion();
     }
@@ -1814,13 +1762,12 @@
     public void testStatsProviderWarningAndLimitReached() throws Exception {
         final int CYCLE_DAY = 15;
 
-        final NetworkStats stats = new NetworkStats(0L, 1);
-        increaseMockedTotalBytes(stats, 2999, 2000);
+        mDeps.setMockedTotalBytes(UID_A, 2999, 2000);
 
         // Get active mobile network in place
         expectMobileDefaults();
         mService.updateNetworks();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE,
                 Long.MAX_VALUE);
 
         // Set warning to 7KB and limit to 10KB.
@@ -1830,32 +1777,32 @@
         postMsgAndWaitForCompletion();
 
         // Verifies that remaining quotas are set to providers.
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2001L, 5001L);
+        reset(mStatsManager);
 
         // Increase the usage and simulates that limit reached fires earlier by provider,
         // but actually the quota is not yet reached. Verifies that the limit reached leads to
         // a force update and new quotas should be set.
-        increaseMockedTotalBytes(stats, 1000, 999);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000, 999);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, 2L, 3002L);
+        reset(mStatsManager);
 
         // Increase the usage and simulate warning reached, the new warning should be unlimited
         // since service will disable warning quota to stop lower layer from keep triggering
         // warning reached event.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(
                 TEST_IFACE, Long.MAX_VALUE, 1002L);
-        reset(mStatsService);
+        reset(mStatsManager);
 
         // Increase the usage that over the warning and limit, the new limit should set to 1 to
         // block the network traffic.
-        increaseMockedTotalBytes(stats, 1000L, 1000);
+        mDeps.increaseMockedTotalBytes(UID_A, 1000L, 1000);
         triggerOnStatsProviderWarningOrLimitReached();
-        verify(mStatsService).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
-        reset(mStatsService);
+        verify(mStatsManager).setStatsProviderWarningAndLimitAsync(TEST_IFACE, Long.MAX_VALUE, 1L);
+        reset(mStatsManager);
     }
 
     private void enableRestrictedMode(boolean enable) throws Exception {
@@ -2145,7 +2092,7 @@
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
-        verify(mStatsService).advisePersistThreshold(anyLong());
+        verify(mStatsManager).advisePersistThreshold(anyLong());
     }
 
     private static class TestAbstractFuture<T> extends AbstractFuture<T> {
diff --git a/services/tests/servicestests/src/com/android/server/wm/OWNERS b/services/tests/servicestests/src/com/android/server/wm/OWNERS
new file mode 100644
index 0000000..361760d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b109c46..de97730 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4724,7 +4724,7 @@
         /**
          * A priority list of ePDG addresses to be used. Possible values are {@link
          * #EPDG_ADDRESS_STATIC}, {@link #EPDG_ADDRESS_PLMN}, {@link #EPDG_ADDRESS_PCO}, {@link
-         * #EPDG_ADDRESS_CELLULAR_LOC}
+         * #EPDG_ADDRESS_CELLULAR_LOC}, {@link #EPDG_ADDRESS_VISITED_COUNTRY}
          */
         public static final String KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY =
                 KEY_PREFIX + "epdg_address_priority_int_array";
@@ -4899,7 +4899,8 @@
             EPDG_ADDRESS_STATIC,
             EPDG_ADDRESS_PLMN,
             EPDG_ADDRESS_PCO,
-            EPDG_ADDRESS_CELLULAR_LOC
+            EPDG_ADDRESS_CELLULAR_LOC,
+            EPDG_ADDRESS_VISITED_COUNTRY
         })
         public @interface EpdgAddressType {}
 
@@ -4913,6 +4914,8 @@
         public static final int EPDG_ADDRESS_PCO = 2;
         /** Use cellular location to chose epdg server */
         public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
+        /* Use Visited Country FQDN rule*/
+        public static final int EPDG_ADDRESS_VISITED_COUNTRY = 4;
 
         /** @hide */
         @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ba95841..39ab7eb 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -592,6 +592,7 @@
     int RIL_UNSOL_UNTHROTTLE_APN = 1052;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053;
     int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054;
+    int RIL_UNSOL_SLICING_CONFIG_CHANGED = 1055;
 
     /* The following unsols are not defined in RIL.h */
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 4a724b7..2fbcf9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -18,25 +18,29 @@
 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.fail;
 
 import org.junit.Test;
 
 import java.util.HashSet;
 import java.util.Set;
 
-public class VcnCellUnderlyingNetworkTemplateTest {
+public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
     private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
     private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
 
     // Package private for use in VcnGatewayConnectionConfigTest
     static VcnCellUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnCellUnderlyingNetworkTemplate.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
                 .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                 .setOperatorPlmnIds(ALLOWED_PLMN_IDS)
                 .setSimSpecificCarrierIds(ALLOWED_CARRIER_IDS)
                 .setRoaming(MATCH_FORBIDDEN)
@@ -47,8 +51,19 @@
     @Test
     public void testBuilderAndGetters() {
         final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
         assertEquals(ALLOWED_PLMN_IDS, networkPriority.getOperatorPlmnIds());
         assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getSimSpecificCarrierIds());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getRoaming());
@@ -59,8 +74,14 @@
     public void testBuilderAndGettersForDefaultValues() {
         final VcnCellUnderlyingNetworkTemplate networkPriority =
                 new VcnCellUnderlyingNetworkTemplate.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
         assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods.
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
         assertEquals(new HashSet<String>(), networkPriority.getOperatorPlmnIds());
         assertEquals(new HashSet<Integer>(), networkPriority.getSimSpecificCarrierIds());
         assertEquals(MATCH_ANY, networkPriority.getRoaming());
@@ -68,6 +89,29 @@
     }
 
     @Test
+    public void testBuilderRequiresStricterEntryCriteria() {
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnCellUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
     public void testPersistableBundle() {
         final VcnCellUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
         assertEquals(
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
new file mode 100644
index 0000000..399e136
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkTemplateTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.vcn;
+
+public class VcnUnderlyingNetworkTemplateTestBase {
+    // Public for use in NetworkPriorityClassifierTest
+    public static final int TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS = 200;
+    public static final int TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS = 100;
+    public static final int TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS = 400;
+    public static final int TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS = 300;
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index cb5b47b..4063178 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -17,8 +17,6 @@
 
 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.assertTrue;
@@ -28,15 +26,19 @@
 
 import java.util.Set;
 
-public class VcnWifiUnderlyingNetworkTemplateTest {
+public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
     private static final String SSID = "TestWifi";
-    private static final int INVALID_NETWORK_QUALITY = -1;
 
     // Package private for use in VcnGatewayConnectionConfigTest
     static VcnWifiUnderlyingNetworkTemplate getTestNetworkTemplate() {
         return new VcnWifiUnderlyingNetworkTemplate.Builder()
-                .setNetworkQuality(NETWORK_QUALITY_OK)
                 .setMetered(MATCH_FORBIDDEN)
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                 .setSsids(Set.of(SSID))
                 .build();
     }
@@ -44,8 +46,19 @@
     @Test
     public void testBuilderAndGetters() {
         final VcnWifiUnderlyingNetworkTemplate networkPriority = getTestNetworkTemplate();
-        assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
         assertEquals(MATCH_FORBIDDEN, networkPriority.getMetered());
+        assertEquals(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                networkPriority.getMinExitDownstreamBandwidthKbps());
         assertEquals(Set.of(SSID), networkPriority.getSsids());
     }
 
@@ -53,18 +66,37 @@
     public void testBuilderAndGettersForDefaultValues() {
         final VcnWifiUnderlyingNetworkTemplate networkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder().build();
-        assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
         assertEquals(MATCH_ANY, networkPriority.getMetered());
+
+        // Explicitly expect 0, as documented in Javadoc on setter methods..
+        assertEquals(0, networkPriority.getMinEntryUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitUpstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinEntryDownstreamBandwidthKbps());
+        assertEquals(0, networkPriority.getMinExitDownstreamBandwidthKbps());
+
         assertTrue(networkPriority.getSsids().isEmpty());
     }
 
     @Test
-    public void testBuildWithInvalidNetworkQuality() {
+    public void testBuilderRequiresStricterEntryCriteria() {
         try {
             new VcnWifiUnderlyingNetworkTemplate.Builder()
-                    .setNetworkQuality(INVALID_NETWORK_QUALITY);
-            fail("Expected to fail due to the invalid network quality");
-        } catch (Exception expected) {
+                    .setMinUpstreamBandwidthKbps(
+                            TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            new VcnWifiUnderlyingNetworkTemplate.Builder()
+                    .setMinDownstreamBandwidthKbps(
+                            TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                            TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS);
+
+            fail("Expected IAE for exit threshold > entry threshold");
+        } catch (IllegalArgumentException expected) {
         }
     }
 
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 4bb7de8..6c849b5 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -18,7 +18,10 @@
 
 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 android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
 
 import static com.android.server.vcn.VcnTestUtils.setupSystemService;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_ANY;
@@ -76,6 +79,12 @@
     private static final int CARRIER_ID = 1;
     private static final int CARRIER_ID_OTHER = 2;
 
+    private static final int LINK_UPSTREAM_BANDWIDTH_KBPS = 1024;
+    private static final int LINK_DOWNSTREAM_BANDWIDTH_KBPS = 2048;
+
+    private static final int TEST_MIN_UPSTREAM_BANDWIDTH_KBPS = 100;
+    private static final int TEST_MIN_DOWNSTREAM_BANDWIDTH_KBPS = 200;
+
     private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
 
     private static final NetworkCapabilities WIFI_NETWORK_CAPABILITIES =
@@ -83,6 +92,8 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .setSignalStrength(WIFI_RSSI)
                     .setSsid(SSID)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
                     .build();
 
     private static final TelephonyNetworkSpecifier TEL_NETWORK_SPECIFIER =
@@ -93,6 +104,8 @@
                     .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
                     .setSubscriptionIds(Set.of(SUB_ID))
                     .setNetworkSpecifier(TEL_NETWORK_SPECIFIER)
+                    .setLinkUpstreamBandwidthKbps(LINK_UPSTREAM_BANDWIDTH_KBPS)
+                    .setLinkDownstreamBandwidthKbps(LINK_DOWNSTREAM_BANDWIDTH_KBPS)
                     .build();
 
     private static final LinkProperties LINK_PROPERTIES = getLinkPropertiesWithName("test_iface");
@@ -146,7 +159,6 @@
     public void testMatchWithoutNotMeteredBit() {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
                         .setMetered(MATCH_FORBIDDEN)
                         .build();
 
@@ -161,11 +173,133 @@
                         null /* carrierConfig */));
     }
 
+    private void verifyMatchesPriorityRuleForUpstreamBandwidth(
+            int entryUpstreamBandwidth,
+            int exitUpstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinUpstreamBandwidthKbps(entryUpstreamBandwidth, exitUpstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    private void verifyMatchesPriorityRuleForDownstreamBandwidth(
+            int entryDownstreamBandwidth,
+            int exitDownstreamBandwidth,
+            UnderlyingNetworkRecord currentlySelected,
+            boolean expectMatch) {
+        final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
+                new VcnWifiUnderlyingNetworkTemplate.Builder()
+                        .setMinDownstreamBandwidthKbps(
+                                entryDownstreamBandwidth, exitDownstreamBandwidth)
+                        .build();
+
+        assertEquals(
+                expectMatch,
+                checkMatchesPriorityRule(
+                        mVcnContext,
+                        wifiNetworkPriority,
+                        mWifiNetworkRecord,
+                        SUB_GROUP,
+                        mSubscriptionSnapshot,
+                        currentlySelected,
+                        null /* carrierConfig */));
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                null /* currentlySelected */,
+                true);
+    }
+
+    @Test
+    public void testMatchWithEntryDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                null /* currentlySelected */,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitUpstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForUpstreamBandwidth(
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_UPSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthEquals() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS,
+                mWifiNetworkRecord,
+                true);
+    }
+
+    @Test
+    public void testMatchWithExitDownstreamBandwidthTooLow() {
+        verifyMatchesPriorityRuleForDownstreamBandwidth(
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                LINK_DOWNSTREAM_BANDWIDTH_KBPS + 1,
+                mWifiNetworkRecord,
+                false);
+    }
+
     private void verifyMatchWifi(
             boolean isSelectedNetwork, PersistableBundle carrierConfig, boolean expectMatch) {
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .build();
         final UnderlyingNetworkRecord selectedNetworkRecord =
                 isSelectedNetwork ? mWifiNetworkRecord : null;
@@ -214,7 +348,12 @@
         final String nwPrioritySsid = useMatchedSsid ? SSID : SSID_OTHER;
         final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority =
                 new VcnWifiUnderlyingNetworkTemplate.Builder()
-                        .setNetworkQuality(NETWORK_QUALITY_OK)
+                        .setMinUpstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                        .setMinDownstreamBandwidthKbps(
+                                TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                                TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS)
                         .setSsids(Set.of(nwPrioritySsid))
                         .build();
 
@@ -238,7 +377,13 @@
     }
 
     private static VcnCellUnderlyingNetworkTemplate.Builder getCellNetworkPriorityBuilder() {
-        return new VcnCellUnderlyingNetworkTemplate.Builder().setNetworkQuality(NETWORK_QUALITY_OK);
+        return new VcnCellUnderlyingNetworkTemplate.Builder()
+                .setMinUpstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS)
+                .setMinDownstreamBandwidthKbps(
+                        TEST_MIN_ENTRY_DOWNSTREAM_BANDWIDTH_KBPS,
+                        TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS);
     }
 
     @Test
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index 36bea57..95b43cd 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 LOCAL_DIR="$( dirname "${BASH_SOURCE}" )"
 
-if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then
+if git log -n 1 --format='%D' HEAD@{upstream} | grep -q aosp/; then
     # Change appears to be in AOSP
     exit 0
 elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then