diff options
57 files changed, 2672 insertions, 304 deletions
diff --git a/Android.bp b/Android.bp index 36ed8656cfbc..79930abb4887 100644 --- a/Android.bp +++ b/Android.bp @@ -81,7 +81,7 @@ filegroup { ":framework-mca-effect-sources", ":framework-mca-filterfw-sources", ":framework-mca-filterpacks-sources", - ":framework-media-sources", + ":framework-media-non-updatable-sources", ":framework-mms-sources", ":framework-omapi-sources", ":framework-opengl-sources", @@ -419,7 +419,6 @@ filegroup { 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/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index a1a46afcffe6..161a317b2a3c 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -235,7 +235,6 @@ public class PowerExemptionManager { public static final int REASON_LOCKED_BOOT_COMPLETED = 202; /** * All Bluetooth broadcasts. - * @hide */ public static final int REASON_BLUETOOTH_BROADCAST = 203; /** diff --git a/core/api/current.txt b/core/api/current.txt index bb24da1e53fa..d696bc797825 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17980,10 +17980,12 @@ package android.hardware.biometrics { 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 @@ package android.net.vcn { 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 @@ package android.net.vcn { 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 @@ package android.net.vcn { 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 @@ package android.security.identity { 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 @@ package android.security.identity { } 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 @@ package android.security.identity { 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 @@ package android.security.identity { 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 { diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 3300ea0d53a0..b380e6d1393d 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -135,6 +135,7 @@ package android.media { 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 29957e450e24..85560112d37b 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5881,6 +5881,7 @@ package android.media.tv { 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); } @@ -7549,6 +7550,17 @@ package android.net { public static class NetworkStats.Entry { ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long); + method public int getDefaultNetwork(); + method public int getMetered(); + method public long getOperations(); + method public int getRoaming(); + method public long getRxBytes(); + method public long getRxPackets(); + method public int getSet(); + method public int getTag(); + method public long getTxBytes(); + method public long getTxPackets(); + method public int getUid(); } public class RssiCurve implements android.os.Parcelable { @@ -8507,6 +8519,7 @@ package android.os { 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 c6c64b034b60..f94ee8579eab 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -28,8 +28,7 @@ import android.annotation.RequiresPermission; 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 @@ public final class BluetoothAdapter { "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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { } } }; + */ /** @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 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; + //state = mBluetoothGetStateCache.query(null); + state = mService.getState(); } + } catch (RemoteException e) { + Log.e(TAG, "", e); + e.rethrowFromSystemServer(); } finally { mServiceLock.readLock().unlock(); } @@ -2100,6 +2104,7 @@ public final class BluetoothAdapter { } } + /* 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 @@ public final class BluetoothAdapter { } }; + */ /** @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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { } } }; + */ /** @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 @@ public final class BluetoothAdapter { try { mServiceLock.readLock().lock(); if (mService != null) { - return mBluetoothGetAdapterConnectionStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "getConnectionState:", e.getCause()); - } else { - throw e; + return mService.getAdapterConnectionState(); } + //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 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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 93f026860856..1edf5cc96b0d 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -23,8 +23,7 @@ import android.annotation.RequiresPermission; 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 @@ public final class BluetoothDevice implements Parcelable, Attributable { 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 @@ public final class BluetoothDevice implements Parcelable, Attributable { } } }; + */ /** @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 @@ public final class BluetoothDevice implements Parcelable, Attributable { 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 fbee57773173..8e9d7b74bf09 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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2ed00b5d2982..7cd7e7acab12 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2485,6 +2485,8 @@ public abstract class PackageManager { * 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 c8c122da4ab8..9fb70d6a07f5 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -38,6 +38,7 @@ import android.os.Parcel; 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * 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 7648cf241298..d41570682fe1 100644 --- a/core/java/android/hardware/biometrics/CryptoObject.java +++ b/core/java/android/hardware/biometrics/CryptoObject.java @@ -18,6 +18,7 @@ package android.hardware.biometrics; 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 @@ import javax.crypto.Mac; /** * 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 @@ public class CryptoObject { 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 @@ public class CryptoObject { /** * 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 @@ public class CryptoObject { 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 a3d595c23095..480923e2b01d 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -58,6 +58,7 @@ import android.os.PowerManager; 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 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * 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 f50aa991a67c..147138e6712a 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -69,6 +69,8 @@ interface INetworkPolicyManager { 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/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 7ebb646ba3eb..426fc617d5fb 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -535,6 +535,46 @@ public class NetworkPolicyManager { } /** + * 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 5c2855307509..319382691925 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -96,6 +96,141 @@ public class VpnManager { */ 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 febd9b406111..3f7521a12ae9 100644 --- a/core/java/android/net/annotations/PolicyDirection.java +++ b/core/java/android/net/annotations/PolicyDirection.java @@ -24,10 +24,6 @@ import java.lang.annotation.RetentionPolicy; /** * 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 125b5730b2ed..69e63133eb03 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -63,13 +63,22 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork final int opportunisticMatchCriteria = in.getInt(OPPORTUNISTIC_MATCH_KEY); return new VcnCellUnderlyingNetworkTemplate( - networkQuality, meteredMatchCriteria, + minEntryUpstreamBandwidthKbps, + minExitUpstreamBandwidthKbps, + minEntryDownstreamBandwidthKbps, + minExitDownstreamBandwidthKbps, allowedNetworkPlmnIds, allowedSpecificCarrierIds, roamingMatchCriteria, @@ -243,7 +263,6 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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 92956e859fc7..a6830b708c31 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -17,7 +17,6 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static com.android.internal.annotations.VisibleForTesting.Visibility; @@ -169,18 +168,15 @@ public final class VcnGatewayConnectionConfig { 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 60fc936072fb..3a9ca3edded7 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -48,23 +48,6 @@ public abstract class VcnUnderlyingNetworkTemplate { /** @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 @@ public abstract class VcnUnderlyingNetworkTemplate { private final int mNetworkPriorityType; /** @hide */ - static final String NETWORK_QUALITY_KEY = "mNetworkQuality"; + static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; - private final int mNetworkQuality; + private final int mMeteredMatchCriteria; /** @hide */ - static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; + public static final int DEFAULT_MIN_BANDWIDTH_KBPS = 0; - private final int mMeteredMatchCriteria; + /** @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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 @@ public abstract class VcnUnderlyingNetworkTemplate { } /** @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 @@ public abstract class VcnUnderlyingNetworkTemplate { 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 272ca9dd7583..23a07abdf0cb 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -46,8 +46,19 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @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 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** 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 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 591cf814c2dc..c11502f61603 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6235,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/identity/java/android/security/identity/CredentialDataRequest.java b/identity/java/android/security/identity/CredentialDataRequest.java new file mode 100644 index 000000000000..2a47a02405e0 --- /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 000000000000..beb03af46303 --- /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 000000000000..7afe3d448bf9 --- /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 6398cee74cba..8e011053d2a7 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredential.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java @@ -58,14 +58,17 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 @@ class CredstoreIdentityCredential extends IdentityCredential { private boolean mAllowUsingExhaustedKeys = true; private boolean mAllowUsingExpiredKeys = false; + private boolean mIncrementKeyUsageCount = true; @Override public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) { @@ -250,6 +254,11 @@ class CredstoreIdentityCredential extends IdentityCredential { mAllowUsingExpiredKeys = allowUsingExpiredKeys; } + @Override + public void setIncrementKeyUsageCount(boolean incrementKeyUsageCount) { + mIncrementKeyUsageCount = incrementKeyUsageCount; + } + private boolean mOperationHandleSet = false; private long mOperationHandle = 0; @@ -264,7 +273,8 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 @@ class CredstoreIdentityCredential extends IdentityCredential { 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 d8d47424e2e8..fb0880ce3521 100644 --- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java +++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java @@ -126,7 +126,8 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { 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 @@ class CredstoreIdentityCredentialStore extends IdentityCredentialStore { + 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 000000000000..e3c6689a8914 --- /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 1e685856d011..cdf746fc9900 100644 --- a/identity/java/android/security/identity/IdentityCredential.java +++ b/identity/java/android/security/identity/IdentityCredential.java @@ -48,7 +48,9 @@ public abstract class IdentityCredential { * 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 @@ public abstract class IdentityCredential { * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * * @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 @@ public abstract class IdentityCredential { * 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 @@ public abstract class IdentityCredential { * @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 6ccd0e892141..dbb8aaa7796e 100644 --- a/identity/java/android/security/identity/IdentityCredentialStore.java +++ b/identity/java/android/security/identity/IdentityCredentialStore.java @@ -209,6 +209,25 @@ public abstract class IdentityCredentialStore { @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 000000000000..afaafce32798 --- /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 71860d261285..d46f9854b278 100644 --- a/identity/java/android/security/identity/ResultData.java +++ b/identity/java/android/security/identity/ResultData.java @@ -28,7 +28,10 @@ import java.util.Collection; /** * 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/media/java/Android.bp b/media/java/Android.bp index eeaf6e9015ac..c7c1d54cd3c6 100644 --- a/media/java/Android.bp +++ b/media/java/Android.bp @@ -8,7 +8,7 @@ package { } filegroup { - name: "framework-media-sources", + name: "framework-media-non-updatable-sources", srcs: [ "**/*.java", "**/*.aidl", diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java index 19ea2de6a434..d1bb41e70b54 100644 --- a/media/java/android/media/BtProfileConnectionInfo.java +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -34,7 +34,7 @@ public final class BtProfileConnectionInfo implements Parcelable { /** @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 @@ public final class BtProfileConnectionInfo implements Parcelable { } /** + * 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 33acd0d5e0e2..fa0429350a25 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 efd9d1005b05..420f4ef95cd5 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -18,6 +18,7 @@ package android.media.tv; 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.annotation.TestApi; 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.Iterator; 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 @@ public final class TvInputManager { 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 @@ public final class TvInputManager { 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 76b84d98046a..0eb1c311695c 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 73ea663aa37e..75548894c9f0 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/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java index 0d15dffae92d..49aa99b0975b 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 @@ package android.net; 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.SystemService; 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 dalvik.system.CloseGuard; 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 @@ public final class IpSecManager { @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/NetworkStats.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java index b00fea4de269..9d532e7929a6 100644 --- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java +++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStats.java @@ -383,6 +383,95 @@ public final class NetworkStats implements Parcelable { this.operations += another.operations; } + /** + * @return interface name of this entry. + * @hide + */ + @Nullable public String getIface() { + return iface; + } + + /** + * @return the uid of this entry. + */ + public int getUid() { + return uid; + } + + /** + * @return the set state of this entry. Should be one of the following + * values: {@link #SET_DEFAULT}, {@link #SET_FOREGROUND}. + */ + @State public int getSet() { + return set; + } + + /** + * @return the tag value of this entry. + */ + public int getTag() { + return tag; + } + + /** + * @return the metered state. Should be one of the following + * values: {link #METERED_YES}, {link #METERED_NO}. + */ + @Meteredness public int getMetered() { + return metered; + } + + /** + * @return the roaming state. Should be one of the following + * values: {link #ROAMING_YES}, {link #ROAMING_NO}. + */ + @Roaming public int getRoaming() { + return roaming; + } + + /** + * @return the default network state. Should be one of the following + * values: {link #DEFAULT_NETWORK_YES}, {link #DEFAULT_NETWORK_NO}. + */ + @DefaultNetwork public int getDefaultNetwork() { + return defaultNetwork; + } + + /** + * @return the number of received bytes. + */ + public long getRxBytes() { + return rxBytes; + } + + /** + * @return the number of received packets. + */ + public long getRxPackets() { + return rxPackets; + } + + /** + * @return the number of transmitted bytes. + */ + public long getTxBytes() { + return txBytes; + } + + /** + * @return the number of transmitted packets. + */ + public long getTxPackets() { + return txPackets; + } + + /** + * @return the count of network operations performed for this entry. + */ + public long getOperations() { + return operations; + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); @@ -593,7 +682,7 @@ public final class NetworkStats implements Parcelable { * @hide */ @UnsupportedAppUsage - public Entry getValues(int i, Entry recycle) { + public Entry getValues(int i, @Nullable Entry recycle) { final Entry entry = recycle != null ? recycle : new Entry(); entry.iface = iface[i]; entry.uid = uid[i]; 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 4cc18af46653..e15acf3ef616 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -96,6 +96,7 @@ 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; @@ -861,7 +862,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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); @@ -2002,10 +2003,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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()); @@ -2047,6 +2050,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull private final Semaphore mSemaphore; @NonNull final AlertObserver mAlertObserver; @NonNull final CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList; + @NonNull final NetworkPolicyManager mNetworkPolicyManager; @NonNull private final Object mProviderStatsLock = new Object(); @@ -2060,7 +2064,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull String tag, @NonNull INetworkStatsProvider provider, @NonNull Semaphore semaphore, @NonNull AlertObserver alertObserver, - @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList) + @NonNull CopyOnWriteArrayList<NetworkStatsProviderCallbackImpl> cbList, + @NonNull NetworkPolicyManager networkPolicyManager) throws RemoteException { mTag = tag; mProvider = provider; @@ -2068,6 +2073,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mSemaphore = semaphore; mAlertObserver = alertObserver; mStatsProviderCbList = cbList; + mNetworkPolicyManager = networkPolicyManager; } @NonNull @@ -2114,8 +2120,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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/SmartStorageMaintIdler.java b/services/core/java/com/android/server/SmartStorageMaintIdler.java new file mode 100644 index 000000000000..2dff72fdc344 --- /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 2d6170b4cbea..53c8635c4e0a 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.Configuration; 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.storage.StorageSessionController.ExternalStorageServic 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 @@ class StorageManagerService extends IStorageManager.Stub @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 @@ class StorageManagerService extends IStorageManager.Stub } 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub // 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 @@ class StorageManagerService extends IStorageManager.Stub 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/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 0a114b924063..fba8c05065f2 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 @@ public class AudioDeviceInventory { 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/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 03a63b9058a8..8ef42ff97aad 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 class NetworkPolicyManagerInternal { */ 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 class NetworkPolicyManagerInternal { */ 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 3163e7f8443e..e1cbdb7d22fe 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3452,6 +3452,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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); @@ -5661,14 +5690,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @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) { @@ -5710,12 +5731,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index 6db25b7ed583..c96c1ee01a6d 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_TEST; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static com.android.server.VcnManagementService.LOCAL_LOG; @@ -47,6 +45,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription import com.android.server.vcn.VcnContext; import java.util.List; +import java.util.Objects; import java.util.Set; /** @hide */ @@ -121,9 +120,10 @@ class NetworkPriorityClassifier { 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 @@ class NetworkPriorityClassifier { 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 @@ class NetworkPriorityClassifier { } // 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 @@ class NetworkPriorityClassifier { return true; } - private static int getWifiQuality( + private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, UnderlyingNetworkRecord currentlySelected, PersistableBundle carrierConfig) { @@ -196,14 +212,14 @@ class NetworkPriorityClassifier { 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 0792d9bde4b4..df98390ddf05 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.media.IAudioService; 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 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } 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/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index bcb2cf8fa0c3..187b0123c879 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 3ac30d0258a5..a63aa6a918d7 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.PRIVATE_DNS_SET_NO_ERROR; 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.StringParceledListSlice; 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { 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 0cd800564048..9a6f61e7c6cf 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -1746,9 +1746,7 @@ public class NetworkPolicyManagerServiceTest { } 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(mStatsManager).forceUpdate(); 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 000000000000..361760d7f945 --- /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/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index ba9584112b75..39ab7eb0973c 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -592,6 +592,7 @@ public interface RILConstants { 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 4a724b72f5f9..2fbcf9d87bd4 100644 --- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java @@ -18,25 +18,29 @@ package android.net.vcn; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.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 @@ public class VcnCellUnderlyingNetworkTemplateTest { @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 class VcnCellUnderlyingNetworkTemplateTest { 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 @@ public class VcnCellUnderlyingNetworkTemplateTest { } @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 000000000000..399e13600442 --- /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 cb5b47bbdc15..4063178e005d 100644 --- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java @@ -17,8 +17,6 @@ package android.net.vcn; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_ANY; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -28,15 +26,19 @@ import org.junit.Test; 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 @@ public class VcnWifiUnderlyingNetworkTemplateTest { @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 class VcnWifiUnderlyingNetworkTemplateTest { 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 4bb7de8c1fef..6c849b5af888 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 @@ package com.android.server.vcn.routeselection; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.NETWORK_QUALITY_OK; +import static 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { .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 @@ public class NetworkPriorityClassifierTest { .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 class NetworkPriorityClassifierTest { public void testMatchWithoutNotMeteredBit() { final VcnWifiUnderlyingNetworkTemplate wifiNetworkPriority = new VcnWifiUnderlyingNetworkTemplate.Builder() - .setNetworkQuality(NETWORK_QUALITY_OK) .setMetered(MATCH_FORBIDDEN) .build(); @@ -161,11 +173,133 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { 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 @@ public class NetworkPriorityClassifierTest { } 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 36bea57b710f..95b43cdf253d 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 |