Merge "Make certain BluetoothProfile constants being used by CarBluetoothTest into @SystemApi"
diff --git a/Android.bp b/Android.bp
index f57acfa..3046b20 100644
--- a/Android.bp
+++ b/Android.bp
@@ -53,6 +53,7 @@
"core/java/android/view/DisplayAdjustments.java",
],
path: "core/java",
+ visibility: ["//frameworks/base/test-mock"],
}
filegroup {
@@ -73,6 +74,14 @@
}
filegroup {
+ name: "framework-identity-sources",
+ srcs: [
+ "identity/java/**/*.java",
+ ],
+ path: "identity/java",
+}
+
+filegroup {
name: "framework-keystore-sources",
srcs: [
"keystore/java/**/*.java",
@@ -216,6 +225,7 @@
":framework-drm-sources",
":framework-graphics-sources",
":framework-keystore-sources",
+ ":framework-identity-sources",
":framework-location-sources",
":framework-lowpan-sources",
":framework-media-sources",
@@ -238,6 +248,7 @@
":platform-compat-native-aidl",
// AIDL sources from external directories
+ ":credstore_aidl",
":dumpstate_aidl",
":framework_native_aidl",
":gatekeeper_aidl",
@@ -289,6 +300,7 @@
"core/java",
"drm/java",
"graphics/java",
+ "identity/java",
"keystore/java",
"location/java",
"lowpan/java",
diff --git a/api/current.txt b/api/current.txt
index 02ef7c1..68f335f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16637,7 +16637,9 @@
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);
method public javax.crypto.Cipher getCipher();
+ method @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method public javax.crypto.Mac getMac();
method public java.security.Signature getSignature();
}
@@ -17575,7 +17577,9 @@
ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
+ ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull android.security.identity.IdentityCredential);
method @Deprecated public javax.crypto.Cipher getCipher();
+ method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @Deprecated public javax.crypto.Mac getMac();
method @Deprecated public java.security.Signature getSignature();
}
@@ -18287,6 +18291,7 @@
field public static final int CHEROKEE_SUPPLEMENT_ID = 255; // 0xff
field public static final android.icu.lang.UCharacter.UnicodeBlock CHESS_SYMBOLS;
field public static final int CHESS_SYMBOLS_ID = 281; // 0x119
+ field public static final android.icu.lang.UCharacter.UnicodeBlock CHORASMIAN;
field public static final int CHORASMIAN_ID = 301; // 0x12d
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY;
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_COMPATIBILITY_FORMS;
@@ -18315,6 +18320,7 @@
field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E_ID = 256; // 0x100
field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F;
field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F_ID = 274; // 0x112
+ field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G;
field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G_ID = 302; // 0x12e
field public static final int CJK_UNIFIED_IDEOGRAPHS_ID = 71; // 0x47
field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS;
@@ -18365,6 +18371,7 @@
field public static final int DEVANAGARI_ID = 15; // 0xf
field public static final android.icu.lang.UCharacter.UnicodeBlock DINGBATS;
field public static final int DINGBATS_ID = 56; // 0x38
+ field public static final android.icu.lang.UCharacter.UnicodeBlock DIVES_AKURU;
field public static final int DIVES_AKURU_ID = 303; // 0x12f
field public static final android.icu.lang.UCharacter.UnicodeBlock DOGRA;
field public static final int DOGRA_ID = 282; // 0x11a
@@ -22537,6 +22544,7 @@
field public static final android.icu.util.VersionInfo UNICODE_11_0;
field public static final android.icu.util.VersionInfo UNICODE_12_0;
field public static final android.icu.util.VersionInfo UNICODE_12_1;
+ field public static final android.icu.util.VersionInfo UNICODE_13_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -40921,6 +40929,7 @@
public final class SEService {
ctor public SEService(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.se.omapi.SEService.OnConnectedListener);
method @NonNull public android.se.omapi.Reader[] getReaders();
+ method @NonNull public android.se.omapi.Reader getUiccReader(int);
method @NonNull public String getVersion();
method public boolean isConnected();
method public void shutdown();
@@ -41058,6 +41067,139 @@
}
+package android.security.identity {
+
+ public class AccessControlProfile {
+ }
+
+ public static final class AccessControlProfile.Builder {
+ ctor public AccessControlProfile.Builder(@NonNull android.security.identity.AccessControlProfileId);
+ method @NonNull public android.security.identity.AccessControlProfile build();
+ method @NonNull public android.security.identity.AccessControlProfile.Builder setReaderCertificate(@NonNull java.security.cert.X509Certificate);
+ method @NonNull public android.security.identity.AccessControlProfile.Builder setUserAuthenticationRequired(boolean);
+ method @NonNull public android.security.identity.AccessControlProfile.Builder setUserAuthenticationTimeout(long);
+ }
+
+ public class AccessControlProfileId {
+ ctor public AccessControlProfileId(int);
+ method public int getId();
+ }
+
+ public class AlreadyPersonalizedException extends android.security.identity.IdentityCredentialException {
+ ctor public AlreadyPersonalizedException(@NonNull String);
+ ctor public AlreadyPersonalizedException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class CipherSuiteNotSupportedException extends android.security.identity.IdentityCredentialException {
+ ctor public CipherSuiteNotSupportedException(@NonNull String);
+ ctor public CipherSuiteNotSupportedException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class DocTypeNotSupportedException extends android.security.identity.IdentityCredentialException {
+ ctor public DocTypeNotSupportedException(@NonNull String);
+ ctor public DocTypeNotSupportedException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class EphemeralPublicKeyNotFoundException extends android.security.identity.IdentityCredentialException {
+ ctor public EphemeralPublicKeyNotFoundException(@NonNull String);
+ ctor public EphemeralPublicKeyNotFoundException(@NonNull String, @NonNull Throwable);
+ }
+
+ 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 @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 public abstract void setAllowUsingExhaustedKeys(boolean);
+ method public abstract void setAvailableAuthenticationKeys(int, int);
+ method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
+ method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ }
+
+ public class IdentityCredentialException extends java.lang.Exception {
+ ctor public IdentityCredentialException(@NonNull String);
+ ctor public IdentityCredentialException(@NonNull String, @NonNull Throwable);
+ }
+
+ 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 @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);
+ method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context);
+ method @NonNull public abstract String[] getSupportedDocTypes();
+ field public static final int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1; // 0x1
+ }
+
+ public class InvalidReaderSignatureException extends android.security.identity.IdentityCredentialException {
+ ctor public InvalidReaderSignatureException(@NonNull String);
+ ctor public InvalidReaderSignatureException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class InvalidRequestMessageException extends android.security.identity.IdentityCredentialException {
+ ctor public InvalidRequestMessageException(@NonNull String);
+ ctor public InvalidRequestMessageException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class MessageDecryptionException extends android.security.identity.IdentityCredentialException {
+ ctor public MessageDecryptionException(@NonNull String);
+ ctor public MessageDecryptionException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class NoAuthenticationKeyAvailableException extends android.security.identity.IdentityCredentialException {
+ ctor public NoAuthenticationKeyAvailableException(@NonNull String);
+ ctor public NoAuthenticationKeyAvailableException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class PersonalizationData {
+ }
+
+ public static final class PersonalizationData.Builder {
+ ctor public PersonalizationData.Builder();
+ method @NonNull public android.security.identity.PersonalizationData.Builder addAccessControlProfile(@NonNull android.security.identity.AccessControlProfile);
+ method @NonNull public android.security.identity.PersonalizationData build();
+ method @NonNull public android.security.identity.PersonalizationData.Builder setEntry(@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> getNamespaceNames();
+ 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 class SessionTranscriptMismatchException extends android.security.identity.IdentityCredentialException {
+ ctor public SessionTranscriptMismatchException(@NonNull String);
+ ctor public SessionTranscriptMismatchException(@NonNull String, @NonNull Throwable);
+ }
+
+ public class UnknownAuthenticationKeyException extends android.security.identity.IdentityCredentialException {
+ ctor public UnknownAuthenticationKeyException(@NonNull String);
+ ctor public UnknownAuthenticationKeyException(@NonNull String, @NonNull Throwable);
+ }
+
+ public abstract class WritableIdentityCredential {
+ ctor public WritableIdentityCredential();
+ method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[]);
+ method @NonNull public abstract byte[] personalize(@NonNull android.security.identity.PersonalizationData);
+ }
+
+}
+
package android.security.keystore {
public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -45182,7 +45324,7 @@
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
field public static final int LISTEN_NONE = 0; // 0x0
- field @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
@@ -46152,6 +46294,37 @@
package android.telephony.ims {
+ public final class ImsException extends java.lang.Exception {
+ method public int getCode();
+ field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
+ field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
+ field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
+ }
+
+ public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
+ method @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
+ field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
+ field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
+ }
+
+ public static class ImsMmTelManager.CapabilityCallback {
+ ctor public ImsMmTelManager.CapabilityCallback();
+ method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
ctor public ImsReasonInfo(int, int, @Nullable String);
method public int describeContents();
@@ -46335,6 +46508,38 @@
field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2
}
+ public interface RegistrationManager {
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
+ field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
+ field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
+ field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
+ }
+
+ public static class RegistrationManager.RegistrationCallback {
+ ctor public RegistrationManager.RegistrationCallback();
+ method public void onRegistered(int);
+ method public void onRegistering(int);
+ method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
+ }
+
+}
+
+package android.telephony.ims.feature {
+
+ public class MmTelFeature {
+ }
+
+ public static class MmTelFeature.MmTelCapabilities {
+ field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
+ field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
+ field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
+ field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
+ }
+
}
package android.telephony.mbms {
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 07106ba..5c31f41 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -485,6 +485,12 @@
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CHORASMIAN:
+ Missing nullability on field `CHORASMIAN` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G:
+ Missing nullability on field `CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G` in class `class android.icu.lang.UCharacter.UnicodeBlock`
+MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#DIVES_AKURU:
+ Missing nullability on field `DIVES_AKURU` in class `class android.icu.lang.UCharacter.UnicodeBlock`
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
MissingNullability: android.icu.lang.UCharacter.UnicodeBlock#ELYMAIC:
@@ -529,6 +535,8 @@
MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
+MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
+ Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
diff --git a/api/system-current.txt b/api/system-current.txt
index 853ee9d..89a54d9 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1657,6 +1657,7 @@
field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME";
field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
field public static final String EXTRA_REASON = "android.intent.extra.REASON";
+ field @Deprecated public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock";
field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED";
field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME";
@@ -4510,6 +4511,10 @@
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
+ public class NetworkRequest implements android.os.Parcelable {
+ method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
+ }
+
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
}
@@ -4644,6 +4649,15 @@
method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress);
}
+ public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+ ctor public StringNetworkSpecifier(@NonNull String);
+ method public int describeContents();
+ method public boolean satisfiedBy(android.net.NetworkSpecifier);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
+ field @NonNull public final String specifier;
+ }
+
public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
method public boolean satisfiedBy(android.net.NetworkSpecifier);
}
@@ -8829,7 +8843,7 @@
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
- field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
+ field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
@@ -9879,10 +9893,6 @@
ctor public ImsException(@Nullable String);
ctor public ImsException(@Nullable String, int);
ctor public ImsException(@Nullable String, int, @Nullable Throwable);
- method public int getCode();
- field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
- field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
- field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
}
public final class ImsExternalCallState implements android.os.Parcelable {
@@ -9908,23 +9918,13 @@
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
- method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTtyOverVolteEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
@@ -9934,16 +9934,6 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
- field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
- field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
- field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
- }
-
- public static class ImsMmTelManager.CapabilityCallback {
- ctor public ImsMmTelManager.CapabilityCallback();
- method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
}
@Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
@@ -10283,24 +10273,6 @@
method @NonNull public android.telephony.ims.RcsContactUceCapability build();
}
- public interface RegistrationManager {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
- field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
- field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
- }
-
- public static class RegistrationManager.RegistrationCallback {
- ctor public RegistrationManager.RegistrationCallback();
- method public void onRegistered(int);
- method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
- }
-
}
package android.telephony.ims.feature {
@@ -10363,7 +10335,7 @@
method public void onFeatureReady();
method public void onFeatureRemoved();
method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
+ method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
@@ -10379,10 +10351,6 @@
method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
- field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
- field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
- field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
}
@IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 57ee408..5802f6c 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -130,18 +130,6 @@
}
-package android.net {
-
- @Deprecated public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
- ctor public StringNetworkSpecifier(@NonNull String);
- method public int describeContents();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
- field @NonNull public final String specifier;
- }
-
-}
-
package android.net.wifi {
@Deprecated public class BatchedScanResult implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index de35bb3..e4997b7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3248,10 +3248,6 @@
ctor public ImsException(@Nullable String);
ctor public ImsException(@Nullable String, int);
ctor public ImsException(@Nullable String, int, @Nullable Throwable);
- method public int getCode();
- field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
- field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
- field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
}
public final class ImsExternalCallState implements android.os.Parcelable {
@@ -3277,23 +3273,13 @@
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
- method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiModeSetting();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiRoamingModeSetting();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAdvancedCallingSettingEnabled();
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isTtyOverVolteEnabled();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVoWiFiRoamingSettingEnabled();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVoWiFiSettingEnabled();
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVtSettingEnabled();
method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
@@ -3303,16 +3289,6 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean);
method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
- field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1
- field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0
- field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2
- }
-
- public static class ImsMmTelManager.CapabilityCallback {
- ctor public ImsMmTelManager.CapabilityCallback();
- method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities);
}
@Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback {
@@ -3600,24 +3576,6 @@
method public void onProvisioningStringChanged(int, @NonNull String);
}
- public interface RegistrationManager {
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0
- field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2
- field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1
- }
-
- public static class RegistrationManager.RegistrationCallback {
- ctor public RegistrationManager.RegistrationCallback();
- method public void onRegistered(int);
- method public void onRegistering(int);
- method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo);
- method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo);
- }
-
}
package android.telephony.ims.feature {
@@ -3680,7 +3638,7 @@
method public void onFeatureReady();
method public void onFeatureRemoved();
method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
- method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
+ method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus();
method public void setUiTtyMode(int, @Nullable android.os.Message);
method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]);
field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
@@ -3696,10 +3654,6 @@
method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int);
- field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8
- field public static final int CAPABILITY_TYPE_UT = 4; // 0x4
- field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2
- field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1
}
@IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9c7bf1f..9cba7aa 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4011,6 +4011,7 @@
* <p>
* @see #EXTRA_SIM_STATE
* @see #EXTRA_SIM_LOCKED_REASON
+ * @see #EXTRA_REBROADCAST_ON_UNLOCK
*
* @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
* {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
@@ -4187,6 +4188,18 @@
public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED";
/**
+ * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for indicating whether this broadcast
+ * is a rebroadcast on unlock. Defaults to {@code false} if not specified.
+ *
+ * @hide
+ * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or
+ * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED}
+ */
+ @Deprecated
+ @SystemApi
+ public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock";
+
+ /**
* Broadcast Action: indicate that the phone service state has changed.
* The intent will have the following extra values:</p>
* <p>
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1142a07..2497ea9 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.security.identity.IdentityCredential;
import android.text.TextUtils;
import android.util.Log;
@@ -401,6 +402,10 @@
super(mac);
}
+ public CryptoObject(@NonNull IdentityCredential credential) {
+ super(credential);
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -424,6 +429,14 @@
public Mac getMac() {
return super.getMac();
}
+
+ /**
+ * Get {@link IdentityCredential} object.
+ * @return {@link IdentityCredential} object or null if this doesn't contain one.
+ */
+ public @Nullable IdentityCredential getIdentityCredential() {
+ return super.getIdentityCredential();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 787dc66..0af18df 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.annotation.NonNull;
+import android.security.identity.IdentityCredential;
import android.security.keystore.AndroidKeyStoreProvider;
import java.security.Signature;
@@ -26,7 +27,8 @@
/**
* A wrapper class for the crypto objects supported by BiometricPrompt and FingerprintManager.
- * Currently the framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+ * Currently the framework supports {@link Signature}, {@link Cipher}, {@link Mac} and
+ * {@link IdentityCredential} objects.
* @hide
*/
public class CryptoObject {
@@ -44,6 +46,10 @@
mCrypto = mac;
}
+ public CryptoObject(@NonNull IdentityCredential credential) {
+ mCrypto = credential;
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -69,11 +75,23 @@
}
/**
+ * Get {@link IdentityCredential} object.
+ * @return {@link IdentityCredential} object or null if this doesn't contain one.
+ */
+ public IdentityCredential getIdentityCredential() {
+ return mCrypto instanceof IdentityCredential ? (IdentityCredential) mCrypto : null;
+ }
+
+ /**
* @hide
* @return the opId associated with this object or 0 if none
*/
public final long getOpId() {
- return mCrypto != null
- ? AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCrypto) : 0;
+ if (mCrypto == null) {
+ return 0;
+ } else if (mCrypto instanceof IdentityCredential) {
+ return ((IdentityCredential) 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 315af32..16f9688 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -44,6 +44,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.security.identity.IdentityCredential;
import android.util.Slog;
import java.security.Signature;
@@ -125,6 +126,10 @@
super(mac);
}
+ public CryptoObject(@NonNull IdentityCredential credential) {
+ super(credential);
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -148,6 +153,14 @@
public Mac getMac() {
return super.getMac();
}
+
+ /**
+ * Get {@link IdentityCredential} object.
+ * @return {@link IdentityCredential} object or null if this doesn't contain one.
+ */
+ public @Nullable IdentityCredential getIdentityCredential() {
+ return super.getIdentityCredential();
+ }
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 674c58d..e8740c8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3169,10 +3169,10 @@
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkMisc misc) {
return registerNetworkAgent(messenger, ni, lp, nc, score, misc, NetworkProvider.ID_NONE);
}
@@ -3180,10 +3180,10 @@
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
- * @return NetID corresponding to NetworkAgent.
+ * @return Network corresponding to NetworkAgent.
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
- public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkMisc misc, int providerId) {
try {
return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc, providerId);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e6a0379..3aee4d5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -152,7 +152,7 @@
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
- int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+ Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 5f6cc6e..a5f7d53 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -44,9 +44,7 @@
* @hide
*/
public abstract class NetworkAgent extends Handler {
- // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
- // an exception.
- public final int netId;
+ public final Network network;
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
@@ -245,7 +243,7 @@
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+ network = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
new LinkProperties(lp), new NetworkCapabilities(nc), score, misc, providerId);
}
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 824ddb8..e271037 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -115,13 +115,6 @@
*/
private static final int CMD_SET_FILTER = BASE + 3;
- /**
- * Sent by NetworkFactory to ConnectivityService to indicate that a request is
- * unfulfillable.
- * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest).
- */
- public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4;
-
private final Context mContext;
private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
private final String LOG_TAG;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 3be49d5..ee4379a 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -467,6 +467,19 @@
}
/**
+ * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the
+ * provided {@link NetworkCapabilities}.
+ *
+ * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
+ * satisfy any request.
+ * @hide
+ */
+ @SystemApi
+ public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
+ return networkCapabilities.satisfiedByNetworkCapabilities(nc);
+ }
+
+ /**
* @see Builder#addTransportType(int)
*/
public boolean hasTransport(@Transport int transportType) {
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 530c84a..83dbc63 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -26,16 +26,7 @@
import java.util.Objects;
-/**
- * @deprecated use other subclass of {@link android.net.NetworkSpecifier},
- * eg. {@link android.net.TelephonyNetworkSpecifier},
- * {@link android.net.wifi.WifiNetworkSpecifier} instead.
- * @see {@link android.net.NetworkRequest#setNetworkSpecifier(String)} for details.
- * @removed this class was tentatively made SystemApi in December 2019 in the scramble for
- * publishing mainline APIs, it should be removed before R release is published.
- * @hide
- */
-@Deprecated
+/** @hide */
@SystemApi
public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**
diff --git a/core/java/android/os/AppZygote.java b/core/java/android/os/AppZygote.java
index 6daa5b4..9257496 100644
--- a/core/java/android/os/AppZygote.java
+++ b/core/java/android/os/AppZygote.java
@@ -21,6 +21,8 @@
import com.android.internal.annotations.GuardedBy;
+import dalvik.system.VMRuntime;
+
/**
* AppZygote is responsible for interfacing with an application-specific zygote.
*
@@ -113,7 +115,7 @@
"app_zygote", // seInfo
abi, // abi
abi, // acceptedAbiList
- null, // instructionSet
+ VMRuntime.getInstructionSet(abi), // instructionSet
mZygoteUidGidMin,
mZygoteUidGidMax);
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index 00060ab..a5c5c61 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -98,6 +98,8 @@
private static final String TAG = "OMAPI.SEService";
+ private static final String UICC_TERMINAL = "SIM";
+
private final Object mLock = new Object();
/** The client context (e.g. activity). */
@@ -190,32 +192,33 @@
* is of length 0.
*/
public @NonNull Reader[] getReaders() {
- if (mSecureElementService == null) {
- throw new IllegalStateException("service not connected to system");
- }
- String[] readerNames;
- try {
- readerNames = mSecureElementService.getReaders();
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ loadReaders();
- Reader[] readers = new Reader[readerNames.length];
- int i = 0;
- for (String readerName : readerNames) {
- if (mReaders.get(readerName) == null) {
- try {
- mReaders.put(readerName, new Reader(this, readerName,
- getReader(readerName)));
- readers[i++] = mReaders.get(readerName);
- } catch (Exception e) {
- Log.e(TAG, "Error adding Reader: " + readerName, e);
- }
- } else {
- readers[i++] = mReaders.get(readerName);
- }
- }
- return readers;
+ return mReaders.values().toArray(new Reader[0]);
+ }
+
+ /**
+ * Obtain a UICC Reader instance with specific slot number from the SecureElementService
+ *
+ * @param slotNumber The index of the uicc slot. The index starts from 1.
+ * @throws IllegalArgumentException if the reader object corresponding to the uiccSlotNumber
+ * is not exist.
+ * @return A Reader object for this uicc slot.
+ */
+ public @NonNull Reader getUiccReader(int slotNumber) {
+ if (slotNumber < 1) {
+ throw new IllegalArgumentException("slotNumber should be larger than 0");
+ }
+ loadReaders();
+
+ String readerName = UICC_TERMINAL + slotNumber;
+ Reader reader = mReaders.get(readerName);
+
+ if (reader == null) {
+ throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist");
+ }
+
+ return reader;
}
/**
@@ -270,4 +273,30 @@
throw new IllegalStateException(e.getMessage());
}
}
+
+ /**
+ * Load available Secure Element Readers
+ */
+ private void loadReaders() {
+ if (mSecureElementService == null) {
+ throw new IllegalStateException("service not connected to system");
+ }
+ String[] readerNames;
+ try {
+ readerNames = mSecureElementService.getReaders();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+
+ for (String readerName : readerNames) {
+ if (mReaders.get(readerName) == null) {
+ try {
+ mReaders.put(readerName, new Reader(this, readerName,
+ getReader(readerName)));
+ } catch (Exception e) {
+ Log.e(TAG, "Error adding Reader: " + readerName, e);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e08a06a..f886cf5 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -178,6 +178,10 @@
* Listen for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
* background and foreground calls.
*
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
* @hide
*/
@RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
@@ -187,13 +191,13 @@
/**
* Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
*
- * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
* or the calling app has carrier privileges
* (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onPreciseDataConnectionStateChanged
*/
- @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
/**
@@ -318,26 +322,36 @@
* Listen for call disconnect causes which contains {@link DisconnectCause} and
* {@link PreciseDisconnectCause}.
*
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
*/
@RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000;
/**
* Listen for changes to the call attributes of a currently active call.
- * {@more}
- * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
- * READ_PRECISE_PHONE_STATE}
+ *
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
*
* @see #onCallAttributesChanged
* @hide
*/
@SystemApi
+ @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 0x04000000;
/**
* Listen for IMS call disconnect causes which contains
* {@link android.telephony.ims.ImsReasonInfo}
*
+ * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
+ * or the calling app has carrier privileges
+ * (see {@link TelephonyManager#hasCarrierPrivileges}).
+ *
* @see #onImsCallDisconnectCauseChanged(ImsReasonInfo)
*/
@RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE))
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 78ccba4..486df62 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1063,22 +1063,16 @@
DropCapabilitiesBoundingSet(fail_fn);
- bool use_native_bridge = !is_system_server &&
- instruction_set.has_value() &&
- android::NativeBridgeAvailable() &&
- android::NeedsNativeBridge(instruction_set.value().c_str());
+ bool need_pre_initialize_native_bridge =
+ !is_system_server &&
+ instruction_set.has_value() &&
+ android::NativeBridgeAvailable() &&
+ // Native bridge may be already initialized if this
+ // is an app forked from app-zygote.
+ !android::NativeBridgeInitialized() &&
+ android::NeedsNativeBridge(instruction_set.value().c_str());
- if (use_native_bridge && !app_data_dir.has_value()) {
- // The app_data_dir variable should never be empty if we need to use a
- // native bridge. In general, app_data_dir will never be empty for normal
- // applications. It can only happen in special cases (for isolated
- // processes which are not associated with any app). These are launched by
- // the framework and should not be emulated anyway.
- use_native_bridge = false;
- ALOGW("Native bridge will not be used because managed_app_data_dir == nullptr.");
- }
-
- MountEmulatedStorage(uid, mount_external, use_native_bridge, fail_fn);
+ MountEmulatedStorage(uid, mount_external, need_pre_initialize_native_bridge, fail_fn);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
@@ -1094,11 +1088,12 @@
SetGids(env, gids, fail_fn);
SetRLimits(env, rlimits, fail_fn);
- if (use_native_bridge) {
- // Due to the logic behind use_native_bridge we know that both app_data_dir
- // and instruction_set contain values.
- android::PreInitializeNativeBridge(app_data_dir.value().c_str(),
- instruction_set.value().c_str());
+ if (need_pre_initialize_native_bridge) {
+ // Due to the logic behind need_pre_initialize_native_bridge we know that
+ // instruction_set contains a value.
+ android::PreInitializeNativeBridge(
+ app_data_dir.has_value() ? app_data_dir.value().c_str() : nullptr,
+ instruction_set.value().c_str());
}
if (setresgid(gid, gid, gid) == -1) {
diff --git a/core/tests/overlaytests/host/Android.bp b/core/tests/overlaytests/host/Android.bp
new file mode 100644
index 0000000..a2fcef5
--- /dev/null
+++ b/core/tests/overlaytests/host/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_test_host {
+ name: "OverlayHostTests",
+ srcs: ["src/**/*.java"],
+ libs: ["tradefed"],
+ test_suites: ["general-tests"],
+ target_required: [
+ "OverlayHostTests_NonPlatformSignatureOverlay",
+ "OverlayHostTests_PlatformSignatureStaticOverlay",
+ "OverlayHostTests_PlatformSignatureOverlay",
+ "OverlayHostTests_UpdateOverlay",
+ "OverlayHostTests_FrameworkOverlayV1",
+ "OverlayHostTests_FrameworkOverlayV2",
+ "OverlayHostTests_AppOverlayV1",
+ "OverlayHostTests_AppOverlayV2",
+ ],
+}
diff --git a/core/tests/overlaytests/host/Android.mk b/core/tests/overlaytests/host/Android.mk
index e7348d5..d58d939 100644
--- a/core/tests/overlaytests/host/Android.mk
+++ b/core/tests/overlaytests/host/Android.mk
@@ -14,23 +14,6 @@
LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under,src)
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := OverlayHostTests
-LOCAL_JAVA_LIBRARIES := tradefed
-LOCAL_COMPATIBILITY_SUITE := general-tests
-LOCAL_TARGET_REQUIRED_MODULES := \
- OverlayHostTests_NonPlatformSignatureOverlay \
- OverlayHostTests_PlatformSignatureStaticOverlay \
- OverlayHostTests_PlatformSignatureOverlay \
- OverlayHostTests_UpdateOverlay \
- OverlayHostTests_FrameworkOverlayV1 \
- OverlayHostTests_FrameworkOverlayV2 \
- OverlayHostTests_AppOverlayV1 \
- OverlayHostTests_AppOverlayV2
-include $(BUILD_HOST_JAVA_LIBRARY)
-
# Include to build test-apps.
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a818119..eec2072 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -67,7 +67,6 @@
<privapp-permissions package="com.android.managedprovisioning">
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_CONFIGURATION"/>
- <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.CRYPT_KEEPER"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
@@ -362,7 +361,6 @@
</privapp-permissions>
<privapp-permissions package="com.android.vpndialogs">
- <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
diff --git a/identity/MODULE_LICENSE_APACHE2 b/identity/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/identity/MODULE_LICENSE_APACHE2
diff --git a/identity/NOTICE b/identity/NOTICE
new file mode 100644
index 0000000..64aaa8d
--- /dev/null
+++ b/identity/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2009, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/identity/OWNERS b/identity/OWNERS
new file mode 100644
index 0000000..533d90b
--- /dev/null
+++ b/identity/OWNERS
@@ -0,0 +1,3 @@
+swillden@google.com
+zeuthen@google.com
+
diff --git a/identity/java/android/security/identity/AccessControlProfile.java b/identity/java/android/security/identity/AccessControlProfile.java
new file mode 100644
index 0000000..10e451c
--- /dev/null
+++ b/identity/java/android/security/identity/AccessControlProfile.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * A class used to specify access controls.
+ */
+public class AccessControlProfile {
+ private AccessControlProfileId mAccessControlProfileId = new AccessControlProfileId(0);
+ private X509Certificate mReaderCertificate = null;
+ private boolean mUserAuthenticationRequired = true;
+ private long mUserAuthenticationTimeout = 0;
+
+ private AccessControlProfile() {
+ }
+
+ AccessControlProfileId getAccessControlProfileId() {
+ return mAccessControlProfileId;
+ }
+
+ long getUserAuthenticationTimeout() {
+ return mUserAuthenticationTimeout;
+ }
+
+ boolean isUserAuthenticationRequired() {
+ return mUserAuthenticationRequired;
+ }
+
+ X509Certificate getReaderCertificate() {
+ return mReaderCertificate;
+ }
+
+ /**
+ * A builder for {@link AccessControlProfile}.
+ */
+ public static final class Builder {
+ private AccessControlProfile mProfile;
+
+ /**
+ * Each access control profile has numeric identifier that must be unique within the
+ * context of a Credential and may be used to reference the profile.
+ *
+ * <p>By default, the resulting {@link AccessControlProfile} will require user
+ * authentication with a timeout of zero, thus requiring the holder to authenticate for
+ * every presentation where data elements using this access control profile is used.</p>
+ *
+ * @param accessControlProfileId the access control profile identifier.
+ */
+ public Builder(@NonNull AccessControlProfileId accessControlProfileId) {
+ mProfile = new AccessControlProfile();
+ mProfile.mAccessControlProfileId = accessControlProfileId;
+ }
+
+ /**
+ * Set whether user authentication is required.
+ *
+ * <p>This should be used sparingly since disabling user authentication on just a single
+ * data element can easily create a
+ * <a href="https://en.wikipedia.org/wiki/Relay_attack">Relay Attack</a> if the device
+ * on which the credential is stored is compromised.</p>
+ *
+ * @param userAuthenticationRequired Set to true if user authentication is required,
+ * false otherwise.
+ * @return The builder.
+ */
+ public @NonNull Builder setUserAuthenticationRequired(boolean userAuthenticationRequired) {
+ mProfile.mUserAuthenticationRequired = userAuthenticationRequired;
+ return this;
+ }
+
+ /**
+ * Sets the authentication timeout to use.
+ *
+ * <p>The authentication timeout specifies the amount of time, in milliseconds, for which a
+ * user authentication is valid, if user authentication is required (see
+ * {@link #setUserAuthenticationRequired(boolean)}).</p>
+ *
+ * <p>If the timeout is zero, then authentication is always required for each reader
+ * session.</p>
+ *
+ * @param userAuthenticationTimeoutMillis the authentication timeout, in milliseconds.
+ * @return The builder.
+ */
+ public @NonNull Builder setUserAuthenticationTimeout(long userAuthenticationTimeoutMillis) {
+ mProfile.mUserAuthenticationTimeout = userAuthenticationTimeoutMillis;
+ return this;
+ }
+
+ /**
+ * Sets the reader certificate to use when checking access control.
+ *
+ * <p>If set, this is checked against the certificate chain presented by
+ * reader. The access check is fulfilled only if one of the certificates
+ * in the chain, matches the certificate set by this method.</p>
+ *
+ * @param readerCertificate the certificate to use for the access control check.
+ * @return The builder.
+ */
+ public @NonNull Builder setReaderCertificate(@NonNull X509Certificate readerCertificate) {
+ mProfile.mReaderCertificate = readerCertificate;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link AccessControlProfile} from the data supplied to the builder.
+ *
+ * @return The created {@link AccessControlProfile} object.
+ */
+ public @NonNull AccessControlProfile build() {
+ return mProfile;
+ }
+ }
+}
diff --git a/identity/java/android/security/identity/AccessControlProfileId.java b/identity/java/android/security/identity/AccessControlProfileId.java
new file mode 100644
index 0000000..3d59450
--- /dev/null
+++ b/identity/java/android/security/identity/AccessControlProfileId.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+/**
+ * A class used to wrap an access control profile identifiers.
+ */
+public class AccessControlProfileId {
+ private int mId = 0;
+
+ /**
+ * Constructs a new object holding a numerical identifier.
+ *
+ * @param id the identifier.
+ */
+ public AccessControlProfileId(int id) {
+ this.mId = id;
+ }
+
+ /**
+ * Gets the numerical identifier wrapped by this object.
+ *
+ * @return the identifier.
+ */
+ public int getId() {
+ return this.mId;
+ }
+}
diff --git a/identity/java/android/security/identity/AlreadyPersonalizedException.java b/identity/java/android/security/identity/AlreadyPersonalizedException.java
new file mode 100644
index 0000000..1933882
--- /dev/null
+++ b/identity/java/android/security/identity/AlreadyPersonalizedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to create a credential which already exists.
+ */
+public class AlreadyPersonalizedException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link AlreadyPersonalizedException} exception.
+ *
+ * @param message the detail message.
+ */
+ public AlreadyPersonalizedException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link AlreadyPersonalizedException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public AlreadyPersonalizedException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/CipherSuiteNotSupportedException.java b/identity/java/android/security/identity/CipherSuiteNotSupportedException.java
new file mode 100644
index 0000000..e7a6c89
--- /dev/null
+++ b/identity/java/android/security/identity/CipherSuiteNotSupportedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to use a cipher suite which isn't supported.
+ */
+public class CipherSuiteNotSupportedException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link CipherSuiteNotSupportedException} exception.
+ *
+ * @param message the detail message.
+ */
+ public CipherSuiteNotSupportedException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link CipherSuiteNotSupportedException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public CipherSuiteNotSupportedException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
new file mode 100644
index 0000000..c520331
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.nio.ByteBuffer;
+import java.security.InvalidAlgorithmParameterException;
+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.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Map;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyAgreement;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+class CredstoreIdentityCredential extends IdentityCredential {
+
+ private static final String TAG = "CredstoreIdentityCredential";
+ private String mCredentialName;
+ private @IdentityCredentialStore.Ciphersuite int mCipherSuite;
+ private Context mContext;
+ private ICredential mBinder;
+
+ CredstoreIdentityCredential(Context context, String credentialName,
+ @IdentityCredentialStore.Ciphersuite int cipherSuite,
+ ICredential binder) {
+ mContext = context;
+ mCredentialName = credentialName;
+ mCipherSuite = cipherSuite;
+ mBinder = binder;
+ }
+
+ private KeyPair mEphemeralKeyPair = null;
+ private SecretKey mSecretKey = null;
+ private SecretKey mReaderSecretKey = null;
+ private int mEphemeralCounter;
+ private int mReadersExpectedEphemeralCounter;
+
+ 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.createEphemeralKeyPair();
+ 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.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ } catch (KeyStoreException
+ | CertificateException
+ | UnrecoverableKeyException
+ | NoSuchAlgorithmException
+ | IOException e) {
+ throw new RuntimeException("Unexpected exception ", e);
+ }
+ }
+
+ @Override
+ public @NonNull KeyPair createEphemeralKeyPair() {
+ 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);
+ }
+
+ ensureEphemeralKeyPair();
+
+ try {
+ KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+ ka.init(mEphemeralKeyPair.getPrivate());
+ ka.doPhase(readerEphemeralPublicKey, true);
+ byte[] sharedSecret = ka.generateSecret();
+
+ byte[] salt = new byte[1];
+ byte[] info = new byte[0];
+
+ salt[0] = 0x01;
+ byte[] derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+ mSecretKey = new SecretKeySpec(derivedKey, "AES");
+
+ salt[0] = 0x00;
+ derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
+ mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
+
+ mEphemeralCounter = 0;
+ mReadersExpectedEphemeralCounter = 0;
+
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Error performing key agreement", e);
+ }
+ }
+
+ @Override
+ public @NonNull byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext) {
+ byte[] messageCiphertextAndAuthTag = null;
+ try {
+ ByteBuffer iv = ByteBuffer.allocate(12);
+ iv.putInt(0, 0x00000000);
+ iv.putInt(4, 0x00000001);
+ iv.putInt(8, mEphemeralCounter);
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ GCMParameterSpec encryptionParameterSpec = new GCMParameterSpec(128, iv.array());
+ cipher.init(Cipher.ENCRYPT_MODE, mSecretKey, encryptionParameterSpec);
+ messageCiphertextAndAuthTag = cipher.doFinal(messagePlaintext);
+ } catch (BadPaddingException
+ | IllegalBlockSizeException
+ | NoSuchPaddingException
+ | InvalidKeyException
+ | NoSuchAlgorithmException
+ | InvalidAlgorithmParameterException e) {
+ throw new RuntimeException("Error encrypting message", e);
+ }
+ mEphemeralCounter += 1;
+ return messageCiphertextAndAuthTag;
+ }
+
+ @Override
+ public @NonNull byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
+ throws MessageDecryptionException {
+ ByteBuffer iv = ByteBuffer.allocate(12);
+ iv.putInt(0, 0x00000000);
+ iv.putInt(4, 0x00000000);
+ iv.putInt(8, mReadersExpectedEphemeralCounter);
+ byte[] plainText = null;
+ try {
+ final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ cipher.init(Cipher.DECRYPT_MODE, mReaderSecretKey,
+ new GCMParameterSpec(128, iv.array()));
+ plainText = cipher.doFinal(messageCiphertext);
+ } catch (BadPaddingException
+ | IllegalBlockSizeException
+ | InvalidAlgorithmParameterException
+ | InvalidKeyException
+ | NoSuchAlgorithmException
+ | NoSuchPaddingException e) {
+ throw new MessageDecryptionException("Error decrypting message", e);
+ }
+ mReadersExpectedEphemeralCounter += 1;
+ return plainText;
+ }
+
+ @Override
+ public @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain() {
+ try {
+ byte[] certsBlob = mBinder.getCredentialKeyCertificateChain();
+ ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
+
+ Collection<? extends Certificate> certs = null;
+ try {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ certs = factory.generateCertificates(bais);
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error decoding certificates", e);
+ }
+
+ LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ for (Certificate cert : certs) {
+ x509Certs.add((X509Certificate) cert);
+ }
+ return x509Certs;
+ } 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);
+ }
+ }
+
+ private boolean mAllowUsingExhaustedKeys = true;
+
+ @Override
+ public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
+ mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
+ }
+
+ private boolean mOperationHandleSet = false;
+ private long mOperationHandle = 0;
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ @Override
+ public long getCredstoreOperationHandle() {
+ if (!mOperationHandleSet) {
+ try {
+ mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys);
+ 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;
+ }
+
+ @NonNull
+ @Override
+ public ResultData getEntries(
+ @Nullable byte[] requestMessage,
+ @NonNull Map<String, Collection<String>> entriesToRequest,
+ @Nullable byte[] sessionTranscript,
+ @Nullable byte[] readerSignature)
+ throws SessionTranscriptMismatchException, NoAuthenticationKeyAvailableException,
+ InvalidReaderSignatureException, EphemeralPublicKeyNotFoundException,
+ InvalidRequestMessageException {
+
+ RequestNamespaceParcel[] rnsParcels = new RequestNamespaceParcel[entriesToRequest.size()];
+ int n = 0;
+ for (String namespaceName : entriesToRequest.keySet()) {
+ Collection<String> entryNames = entriesToRequest.get(namespaceName);
+ rnsParcels[n] = new RequestNamespaceParcel();
+ rnsParcels[n].namespaceName = namespaceName;
+ rnsParcels[n].entries = new RequestEntryParcel[entryNames.size()];
+ int m = 0;
+ for (String entryName : entryNames) {
+ rnsParcels[n].entries[m] = new RequestEntryParcel();
+ rnsParcels[n].entries[m].name = entryName;
+ m++;
+ }
+ n++;
+ }
+
+ GetEntriesResultParcel resultParcel = null;
+ try {
+ resultParcel = mBinder.getEntries(
+ requestMessage != null ? requestMessage : new byte[0],
+ rnsParcels,
+ sessionTranscript != null ? sessionTranscript : new byte[0],
+ readerSignature != null ? readerSignature : new byte[0],
+ mAllowUsingExhaustedKeys);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_EPHEMERAL_PUBLIC_KEY_NOT_FOUND) {
+ throw new EphemeralPublicKeyNotFoundException(e.getMessage(), e);
+ } else if (e.errorCode == ICredentialStore.ERROR_INVALID_READER_SIGNATURE) {
+ throw new InvalidReaderSignatureException(e.getMessage(), e);
+ } else if (e.errorCode == ICredentialStore.ERROR_NO_AUTHENTICATION_KEY_AVAILABLE) {
+ throw new NoAuthenticationKeyAvailableException(e.getMessage(), e);
+ } else if (e.errorCode == ICredentialStore.ERROR_INVALID_ITEMS_REQUEST_MESSAGE) {
+ throw new InvalidRequestMessageException(e.getMessage(), e);
+ } else if (e.errorCode == ICredentialStore.ERROR_SESSION_TRANSCRIPT_MISMATCH) {
+ throw new SessionTranscriptMismatchException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ byte[] mac = resultParcel.mac;
+ if (mac != null && mac.length == 0) {
+ mac = null;
+ }
+ CredstoreResultData.Builder resultDataBuilder = new CredstoreResultData.Builder(
+ resultParcel.staticAuthenticationData, resultParcel.deviceNameSpaces, mac);
+
+ for (ResultNamespaceParcel resultNamespaceParcel : resultParcel.resultNamespaces) {
+ for (ResultEntryParcel resultEntryParcel : resultNamespaceParcel.entries) {
+ if (resultEntryParcel.status == ICredential.STATUS_OK) {
+ resultDataBuilder.addEntry(resultNamespaceParcel.namespaceName,
+ resultEntryParcel.name, resultEntryParcel.value);
+ } else {
+ resultDataBuilder.addErrorStatus(resultNamespaceParcel.namespaceName,
+ resultEntryParcel.name,
+ resultEntryParcel.status);
+ }
+ }
+ }
+ return resultDataBuilder.build();
+ }
+
+ @Override
+ public void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey) {
+ try {
+ mBinder.setAvailableAuthenticationKeys(keyCount, maxUsesPerKey);
+ } 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 @NonNull Collection<X509Certificate> getAuthKeysNeedingCertification() {
+ try {
+ AuthKeyParcel[] authKeyParcels = mBinder.getAuthKeysNeedingCertification();
+ LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ for (AuthKeyParcel authKeyParcel : authKeyParcels) {
+ Collection<? extends Certificate> certs = null;
+ ByteArrayInputStream bais = new ByteArrayInputStream(authKeyParcel.x509cert);
+ certs = factory.generateCertificates(bais);
+ if (certs.size() != 1) {
+ throw new RuntimeException("Returned blob yields more than one X509 cert");
+ }
+ X509Certificate authKeyCert = (X509Certificate) certs.iterator().next();
+ x509Certs.add(authKeyCert);
+ }
+ return x509Certs;
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error decoding authenticationKey", e);
+ } 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 storeStaticAuthenticationData(X509Certificate authenticationKey,
+ byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ try {
+ AuthKeyParcel authKeyParcel = new AuthKeyParcel();
+ authKeyParcel.x509cert = authenticationKey.getEncoded();
+ mBinder.storeStaticAuthenticationData(authKeyParcel, staticAuthData);
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException("Error encoding authenticationKey", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) {
+ throw new UnknownAuthenticationKeyException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull int[] getAuthenticationDataUsageCount() {
+ try {
+ int[] usageCount = mBinder.getAuthenticationDataUsageCount();
+ return usageCount;
+ } 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);
+ }
+ }
+}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
new file mode 100644
index 0000000..dcc6b95
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.ServiceManager;
+
+class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
+
+ private static final String TAG = "CredstoreIdentityCredentialStore";
+
+ private Context mContext = null;
+ private ICredentialStore mStore = null;
+
+ private CredstoreIdentityCredentialStore(@NonNull Context context, ICredentialStore store) {
+ mContext = context;
+ mStore = store;
+ }
+
+ static CredstoreIdentityCredentialStore getInstanceForType(@NonNull Context context,
+ int credentialStoreType) {
+ ICredentialStoreFactory storeFactory =
+ ICredentialStoreFactory.Stub.asInterface(
+ ServiceManager.getService("android.security.identity"));
+
+ ICredentialStore credStore = null;
+ try {
+ credStore = storeFactory.getCredentialStore(credentialStoreType);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_GENERIC) {
+ return null;
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ if (credStore == null) {
+ return null;
+ }
+
+ return new CredstoreIdentityCredentialStore(context, credStore);
+ }
+
+ private static CredstoreIdentityCredentialStore sInstanceDefault = null;
+ private static CredstoreIdentityCredentialStore sInstanceDirectAccess = null;
+
+ public static @Nullable IdentityCredentialStore getInstance(@NonNull Context context) {
+ if (sInstanceDefault == null) {
+ sInstanceDefault = getInstanceForType(context,
+ ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DEFAULT);
+ }
+ return sInstanceDefault;
+ }
+
+ public static @Nullable IdentityCredentialStore getDirectAccessInstance(@NonNull
+ Context context) {
+ if (sInstanceDirectAccess == null) {
+ sInstanceDirectAccess = getInstanceForType(context,
+ ICredentialStoreFactory.CREDENTIAL_STORE_TYPE_DIRECT_ACCESS);
+ }
+ return sInstanceDirectAccess;
+ }
+
+ @Override
+ public @NonNull String[] getSupportedDocTypes() {
+ try {
+ SecurityHardwareInfoParcel info;
+ info = mStore.getSecurityHardwareInfo();
+ return info.supportedDocTypes;
+ } 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 @NonNull WritableIdentityCredential createCredential(
+ @NonNull String credentialName,
+ @NonNull String docType) throws AlreadyPersonalizedException,
+ DocTypeNotSupportedException {
+ try {
+ IWritableCredential wc;
+ wc = mStore.createCredential(credentialName, docType);
+ return new CredstoreWritableIdentityCredential(mContext, credentialName, docType, wc);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_ALREADY_PERSONALIZED) {
+ throw new AlreadyPersonalizedException(e.getMessage(), e);
+ } else if (e.errorCode == ICredentialStore.ERROR_DOCUMENT_TYPE_NOT_SUPPORTED) {
+ throw new DocTypeNotSupportedException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override public @Nullable IdentityCredential getCredentialByName(
+ @NonNull String credentialName,
+ @Ciphersuite int cipherSuite) throws CipherSuiteNotSupportedException {
+ try {
+ ICredential credstoreCredential;
+ credstoreCredential = mStore.getCredentialByName(credentialName, cipherSuite);
+ return new CredstoreIdentityCredential(mContext, credentialName, cipherSuite,
+ credstoreCredential);
+ } 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 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);
+ }
+ }
+ }
+
+ @Override
+ public @Nullable byte[] deleteCredentialByName(@NonNull String credentialName) {
+ ICredential credstoreCredential = null;
+ try {
+ try {
+ credstoreCredential = mStore.getCredentialByName(credentialName,
+ CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NO_SUCH_CREDENTIAL) {
+ return null;
+ }
+ }
+ byte[] proofOfDeletion = credstoreCredential.deleteCredential();
+ return proofOfDeletion;
+ } 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);
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreResultData.java b/identity/java/android/security/identity/CredstoreResultData.java
new file mode 100644
index 0000000..ef7afca
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreResultData.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * An object that contains the result of retrieving data from a credential. This is used to return
+ * data requested from a {@link IdentityCredential}.
+ */
+class CredstoreResultData extends ResultData {
+
+ byte[] mStaticAuthenticationData = null;
+ byte[] mAuthenticatedData = null;
+ byte[] mMessageAuthenticationCode = null;
+
+ private Map<String, Map<String, EntryData>> mData = new LinkedHashMap<>();
+
+ private static class EntryData {
+ @Status
+ int mStatus;
+ byte[] mValue;
+
+ EntryData(byte[] value, @Status int status) {
+ this.mValue = value;
+ this.mStatus = status;
+ }
+ }
+
+ CredstoreResultData() {}
+
+ @Override
+ public @NonNull byte[] getAuthenticatedData() {
+ return mAuthenticatedData;
+ }
+
+ @Override
+ public @Nullable byte[] getMessageAuthenticationCode() {
+ return mMessageAuthenticationCode;
+ }
+
+ @Override
+ public @NonNull byte[] getStaticAuthenticationData() {
+ return mStaticAuthenticationData;
+ }
+
+ @Override
+ public @NonNull Collection<String> getNamespaceNames() {
+ return Collections.unmodifiableCollection(mData.keySet());
+ }
+
+ @Override
+ public @Nullable Collection<String> getEntryNames(@NonNull String namespaceName) {
+ Map<String, EntryData> innerMap = mData.get(namespaceName);
+ if (innerMap == null) {
+ return null;
+ }
+ return Collections.unmodifiableCollection(innerMap.keySet());
+ }
+
+ @Override
+ public @Nullable Collection<String> getRetrievedEntryNames(@NonNull String namespaceName) {
+ Map<String, EntryData> innerMap = mData.get(namespaceName);
+ if (innerMap == null) {
+ return null;
+ }
+ LinkedList<String> result = new LinkedList<String>();
+ for (Map.Entry<String, EntryData> entry : innerMap.entrySet()) {
+ if (entry.getValue().mStatus == STATUS_OK) {
+ result.add(entry.getKey());
+ }
+ }
+ return result;
+ }
+
+ private EntryData getEntryData(@NonNull String namespaceName, @NonNull String name) {
+ Map<String, EntryData> innerMap = mData.get(namespaceName);
+ if (innerMap == null) {
+ return null;
+ }
+ return innerMap.get(name);
+ }
+
+ @Override
+ @Status
+ public int getStatus(@NonNull String namespaceName, @NonNull String name) {
+ EntryData value = getEntryData(namespaceName, name);
+ if (value == null) {
+ return STATUS_NOT_REQUESTED;
+ }
+ return value.mStatus;
+ }
+
+ @Override
+ public @Nullable byte[] getEntry(@NonNull String namespaceName, @NonNull String name) {
+ EntryData value = getEntryData(namespaceName, name);
+ if (value == null) {
+ return null;
+ }
+ return value.mValue;
+ }
+
+ static class Builder {
+ private CredstoreResultData mResultData;
+
+ Builder(byte[] staticAuthenticationData,
+ byte[] authenticatedData,
+ byte[] messageAuthenticationCode) {
+ this.mResultData = new CredstoreResultData();
+ this.mResultData.mStaticAuthenticationData = staticAuthenticationData;
+ this.mResultData.mAuthenticatedData = authenticatedData;
+ this.mResultData.mMessageAuthenticationCode = messageAuthenticationCode;
+ }
+
+ private Map<String, EntryData> getOrCreateInnerMap(String namespaceName) {
+ Map<String, EntryData> innerMap = mResultData.mData.get(namespaceName);
+ if (innerMap == null) {
+ innerMap = new LinkedHashMap<>();
+ mResultData.mData.put(namespaceName, innerMap);
+ }
+ return innerMap;
+ }
+
+ Builder addEntry(String namespaceName, String name, byte[] value) {
+ Map<String, EntryData> innerMap = getOrCreateInnerMap(namespaceName);
+ innerMap.put(name, new EntryData(value, STATUS_OK));
+ return this;
+ }
+
+ Builder addErrorStatus(String namespaceName, String name, @Status int status) {
+ Map<String, EntryData> innerMap = getOrCreateInnerMap(namespaceName);
+ innerMap.put(name, new EntryData(null, status));
+ return this;
+ }
+
+ CredstoreResultData build() {
+ return mResultData;
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
new file mode 100644
index 0000000..335636c
--- /dev/null
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.security.GateKeeper;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.LinkedList;
+
+class CredstoreWritableIdentityCredential extends WritableIdentityCredential {
+
+ private static final String TAG = "CredstoreWritableIdentityCredential";
+
+ private String mDocType;
+ private String mCredentialName;
+ private Context mContext;
+ private IWritableCredential mBinder;
+
+ CredstoreWritableIdentityCredential(Context context,
+ @NonNull String credentialName,
+ @NonNull String docType,
+ IWritableCredential binder) {
+ mContext = context;
+ mDocType = docType;
+ mCredentialName = credentialName;
+ mBinder = binder;
+ }
+
+ @NonNull @Override
+ public Collection<X509Certificate> getCredentialKeyCertificateChain(@NonNull byte[] challenge) {
+ try {
+ byte[] certsBlob = mBinder.getCredentialKeyCertificateChain(challenge);
+ ByteArrayInputStream bais = new ByteArrayInputStream(certsBlob);
+
+ Collection<? extends Certificate> certs = null;
+ try {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ certs = factory.generateCertificates(bais);
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error decoding certificates", e);
+ }
+
+ LinkedList<X509Certificate> x509Certs = new LinkedList<>();
+ for (Certificate cert : certs) {
+ x509Certs.add((X509Certificate) cert);
+ }
+ return x509Certs;
+ } 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);
+ }
+ }
+
+ @NonNull @Override
+ public byte[] personalize(@NonNull PersonalizationData personalizationData) {
+
+ Collection<AccessControlProfile> accessControlProfiles =
+ personalizationData.getAccessControlProfiles();
+
+ AccessControlProfileParcel[] acpParcels =
+ new AccessControlProfileParcel[accessControlProfiles.size()];
+ boolean usingUserAuthentication = false;
+ int n = 0;
+ for (AccessControlProfile profile : accessControlProfiles) {
+ acpParcels[n] = new AccessControlProfileParcel();
+ acpParcels[n].id = profile.getAccessControlProfileId().getId();
+ X509Certificate cert = profile.getReaderCertificate();
+ if (cert != null) {
+ try {
+ acpParcels[n].readerCertificate = cert.getEncoded();
+ } catch (CertificateException e) {
+ throw new RuntimeException("Error encoding reader certificate", e);
+ }
+ } else {
+ acpParcels[n].readerCertificate = new byte[0];
+ }
+ acpParcels[n].userAuthenticationRequired = profile.isUserAuthenticationRequired();
+ acpParcels[n].userAuthenticationTimeoutMillis = profile.getUserAuthenticationTimeout();
+ if (profile.isUserAuthenticationRequired()) {
+ usingUserAuthentication = true;
+ }
+ n++;
+ }
+
+ Collection<String> namespaceNames = personalizationData.getNamespaceNames();
+
+ EntryNamespaceParcel[] ensParcels = new EntryNamespaceParcel[namespaceNames.size()];
+ n = 0;
+ for (String namespaceName : namespaceNames) {
+ PersonalizationData.NamespaceData nsd =
+ personalizationData.getNamespaceData(namespaceName);
+
+ ensParcels[n] = new EntryNamespaceParcel();
+ ensParcels[n].namespaceName = namespaceName;
+
+ Collection<String> entryNames = nsd.getEntryNames();
+ EntryParcel[] eParcels = new EntryParcel[entryNames.size()];
+ int m = 0;
+ for (String entryName : entryNames) {
+ eParcels[m] = new EntryParcel();
+ eParcels[m].name = entryName;
+ eParcels[m].value = nsd.getEntryValue(entryName);
+ Collection<AccessControlProfileId> acpIds =
+ nsd.getAccessControlProfileIds(entryName);
+ eParcels[m].accessControlProfileIds = new int[acpIds.size()];
+ int o = 0;
+ for (AccessControlProfileId acpId : acpIds) {
+ eParcels[m].accessControlProfileIds[o++] = acpId.getId();
+ }
+ m++;
+ }
+ ensParcels[n].entries = eParcels;
+ n++;
+ }
+
+ // Note: The value 0 is used to convey that no user-authentication is needed for this
+ // credential. This is to allow creating credentials w/o user authentication on devices
+ // where Secure lock screen is not enabled.
+ long secureUserId = 0;
+ if (usingUserAuthentication) {
+ secureUserId = getRootSid();
+ }
+ try {
+ byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
+ secureUserId);
+ return personalizationReceipt;
+ } 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);
+ }
+ }
+
+ private static long getRootSid() {
+ long rootSid = GateKeeper.getSecureUserId();
+ if (rootSid == 0) {
+ throw new IllegalStateException("Secure lock screen must be enabled"
+ + " to create credentials requiring user authentication");
+ }
+ return rootSid;
+ }
+
+
+}
diff --git a/identity/java/android/security/identity/DocTypeNotSupportedException.java b/identity/java/android/security/identity/DocTypeNotSupportedException.java
new file mode 100644
index 0000000..754e44a
--- /dev/null
+++ b/identity/java/android/security/identity/DocTypeNotSupportedException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to create a credential with an unsupported document type.
+ */
+public class DocTypeNotSupportedException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link DocTypeNotSupportedException} exception.
+ *
+ * @param message the detail message.
+ */
+ public DocTypeNotSupportedException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link DocTypeNotSupportedException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public DocTypeNotSupportedException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java b/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java
new file mode 100644
index 0000000..265f271
--- /dev/null
+++ b/identity/java/android/security/identity/EphemeralPublicKeyNotFoundException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if the ephemeral public key was not found in the session transcript
+ * passed to {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])}.
+ */
+public class EphemeralPublicKeyNotFoundException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link EphemeralPublicKeyNotFoundException} exception.
+ *
+ * @param message the detail message.
+ */
+ public EphemeralPublicKeyNotFoundException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link EphemeralPublicKeyNotFoundException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public EphemeralPublicKeyNotFoundException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
new file mode 100644
index 0000000..bd43919
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Class used to read data from a previously provisioned credential.
+ *
+ * Use {@link IdentityCredentialStore#getCredentialByName(String, int)} to get a
+ * {@link IdentityCredential} instance.
+ */
+public abstract class IdentityCredential {
+ /**
+ * @hide
+ */
+ protected IdentityCredential() {}
+
+ /**
+ * Create an ephemeral key pair to use to establish a secure channel with a reader.
+ *
+ * <p>Most applications will use only the public key, and only to send it to the reader,
+ * allowing the private key to be used internally for {@link #encryptMessageToReader(byte[])}
+ * and {@link #decryptMessageFromReader(byte[])}. The private key is also provided for
+ * applications that wish to use a cipher suite that is not supported by
+ * {@link IdentityCredentialStore}.
+ *
+ * @return ephemeral key pair to use to establish a secure channel with a reader.
+ */
+ public @NonNull abstract KeyPair createEphemeralKeyPair();
+
+ /**
+ * Set the ephemeral public key provided by the reader. This must be called before
+ * {@link #encryptMessageToReader} or {@link #decryptMessageFromReader} can be called.
+ *
+ * @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;
+
+ /**
+ * Encrypt a message for transmission to the reader.
+ *
+ * @param messagePlaintext unencrypted message to encrypt.
+ * @return encrypted message.
+ */
+ public @NonNull abstract byte[] encryptMessageToReader(@NonNull byte[] messagePlaintext);
+
+ /**
+ * Decrypt a message received from the reader.
+ *
+ * @param messageCiphertext encrypted message to decrypt.
+ * @return decrypted message.
+ * @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
+ */
+ public @NonNull abstract byte[] decryptMessageFromReader(@NonNull byte[] messageCiphertext)
+ throws MessageDecryptionException;
+
+ /**
+ * Gets the X.509 certificate chain for the CredentialKey which identifies this
+ * credential to the issuing authority. This is the same certificate chain that
+ * was returned by {@link WritableIdentityCredential#getCredentialKeyCertificateChain(byte[])}
+ * when the credential was first created and its Android Keystore extension will
+ * contain the <code>challenge</code> data set at that time. See the documentation
+ * for that method for important information about this certificate chain.
+ *
+ * @return the certificate chain for this credential's CredentialKey.
+ */
+ public @NonNull abstract Collection<X509Certificate> getCredentialKeyCertificateChain();
+
+ /**
+ * Sets whether to allow using an authentication key which use count has been exceeded if no
+ * other key is available. 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.
+ *
+ * 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.
+ */
+ public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
+
+ /**
+ * Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
+ * operation handle.
+ *
+ * @hide
+ */
+ public abstract long getCredstoreOperationHandle();
+
+ /**
+ * Retrieve data entries and associated data from this {@code IdentityCredential}.
+ *
+ * <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 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>If this method returns successfully (i.e. without throwing an exception), it must not be
+ * called again on this instance.
+ *
+ * <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, byt
+ * there are three requirements that must be met to work with this API:
+ * <ul>
+ * <li>The content must be a CBOR-encoded structure.</li>
+ * <li>The CBOR structure must be a map.</li>
+ * <li>The map must contain a tstr key "nameSpaces" whose value contains a map, as described in
+ * the example below.</li>
+ * </ul>
+ *
+ * <p>Here's an example of CBOR which conforms to this requirement:
+ * <pre>
+ * ItemsRequest = {
+ * ? "docType" : DocType,
+ * "nameSpaces" : NameSpaces,
+ * ? "RequestInfo" : {* tstr => any} ; Additional info the reader wants to provide
+ * }
+ *
+ * NameSpaces = {
+ * + NameSpace => DataElements ; Requested data elements for each NameSpace
+ * }
+ *
+ * NameSpace = tstr
+ *
+ * DataElements = {
+ * + DataElement => IntentToRetain
+ * }
+ *
+ * DataElement = tstr
+ * IntentToRetain = bool
+ * </pre>
+ *
+ * <p>If the {@code sessionTranscript} parameter is not {@code null}, it must contain CBOR
+ * data conforming to the following CDDL schema:
+ *
+ * <pre>
+ * SessionTranscript = [
+ * DeviceEngagementBytes,
+ * EReaderKeyBytes
+ * ]
+ *
+ * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+ * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+ * </pre>
+ *
+ * <p>If the SessionTranscript is not empty, a COSE_Key structure for the public part
+ * of the key-pair previously generated by {@link #createEphemeralKeyPair()} must appear
+ * somewhere in {@code DeviceEngagement} and the X and Y coordinates must both be present
+ * in uncompressed form.
+ *
+ * <p>If {@code readerAuth} is not {@code null} it must be the bytes of a COSE_Sign1
+ * structure as defined in RFC 8152. For the payload nil shall be used and the
+ * detached payload is the ReaderAuthentication CBOR described below.
+ * <pre>
+ * ReaderAuthentication = [
+ * "ReaderAuthentication",
+ * SessionTranscript,
+ * ItemsRequestBytes
+ * ]
+ *
+ * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) ; Bytes of ItemsRequest
+ * </pre>
+ *
+ * <p>The public key corresponding to the key used to made signature, can be
+ * found in the {@code x5chain} unprotected header element of the COSE_Sign1
+ * structure (as as described in 'draft-ietf-cose-x509-04'). 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 is returned if, and only if, they are
+ * mentioned in {@code requestMessage}, {@code requestMessage} is signed by the top-most
+ * certificate in {@code readerCertificateChain}, and the data element is configured
+ * with an {@link AccessControlProfile} with a {@link X509Certificate} in
+ * {@code readerCertificateChain}.
+ *
+ * <p>Note that only items referenced in {@code entriesToRequest} are returned - the
+ * {@code requestMessage} parameter is only used to for enforcing reader authentication.
+ *
+ * @param requestMessage If not {@code null}, must contain CBOR data conforming to
+ * the schema mentioned above.
+ * @param entriesToRequest The entries to request, organized as a map of namespace
+ * names with each value being a collection of data elements
+ * in the given namespace.
+ * @param readerSignature COSE_Sign1 structure as described above or {@code null}
+ * if reader authentication is not being used.
+ * @return A {@link ResultData} object containing entry data organized by namespace and a
+ * cryptographically authenticated representation of the same data.
+ * @throws SessionTranscriptMismatchException Thrown when trying use multiple different
+ * session transcripts in the same presentation
+ * session.
+ * @throws NoAuthenticationKeyAvailableException if authentication keys were never
+ * provisioned, the method
+ * {@link #setAvailableAuthenticationKeys(int, int)}
+ * was called with {@code keyCount} set to 0,
+ * the method
+ * {@link #setAllowUsingExhaustedKeys(boolean)}
+ * was called with {@code false} and all
+ * available authentication keys have been
+ * exhausted.
+ * @throws InvalidReaderSignatureException if the reader signature is invalid, or it
+ * doesn't contain a certificate chain, or if
+ * the signature failed to validate.
+ * @throws InvalidRequestMessageException if the requestMessage is malformed.
+ * @throws EphemeralPublicKeyNotFoundException if the ephemeral public key was not found in
+ * the session transcript.
+ */
+ public abstract @NonNull ResultData getEntries(
+ @Nullable byte[] requestMessage,
+ @NonNull Map<String, Collection<String>> entriesToRequest,
+ @Nullable byte[] sessionTranscript,
+ @Nullable byte[] readerSignature)
+ throws SessionTranscriptMismatchException, NoAuthenticationKeyAvailableException,
+ InvalidReaderSignatureException, EphemeralPublicKeyNotFoundException,
+ InvalidRequestMessageException;
+
+ /**
+ * Sets the number of dynamic authentication keys the {@code IdentityCredential} will maintain,
+ * and the number of times each should be used.
+ *
+ * <p>{@code IdentityCredential}s will select the least-used dynamic authentication key each
+ * time {@link #getEntries(byte[], Map, byte[], byte[])} is called. {@code IdentityCredential}s
+ * for which this method has not been called behave as though it had been called wit
+ * {@code keyCount} 0 and {@code maxUsesPerKey} 1.
+ *
+ * @param keyCount The number of active, certified dynamic authentication keys the
+ * {@code IdentityCredential} will try to keep available. This value
+ * must be non-negative.
+ * @param maxUsesPerKey The maximum number of times each of the keys will be used before it's
+ * eligible for replacement. This value must be greater than zero.
+ */
+ public abstract void setAvailableAuthenticationKeys(int keyCount, int maxUsesPerKey);
+
+ /**
+ * Gets a collection of dynamic authentication keys that need certification.
+ *
+ * <p>When there aren't enough certified dynamic authentication keys, either because the key
+ * count has been increased or because one or more keys have reached their usage count, this
+ * method will generate replacement keys and certificates and return them for issuer
+ * certification. The issuer certificates and associated static authentication data must then
+ * be provided back to the {@code IdentityCredential} using
+ * {@link #storeStaticAuthenticationData(X509Certificate, byte[])}.
+ *
+ * <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
+ * can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
+ *
+ * @return A collection of X.509 certificates for dynamic authentication keys that need issuer
+ * certification.
+ */
+ public @NonNull abstract Collection<X509Certificate> getAuthKeysNeedingCertification();
+
+ /**
+ * Store authentication data associated with a dynamic authentication key.
+ *
+ * This should only be called for an authenticated key returned by
+ * {@link #getAuthKeysNeedingCertification()}.
+ *
+ * @param authenticationKey The dynamic authentication key for which certification and
+ * associated static
+ * authentication data is being provided.
+ * @param staticAuthData Static authentication data provided by the issuer that validates
+ * the authenticity
+ * and integrity of the credential data fields.
+ * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ */
+ public abstract void storeStaticAuthenticationData(
+ @NonNull X509Certificate authenticationKey,
+ @NonNull byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException;
+
+ /**
+ * Get the number of times the dynamic authentication keys have been used.
+ *
+ * @return int array of dynamic authentication key usage counts.
+ */
+ public @NonNull abstract int[] getAuthenticationDataUsageCount();
+}
diff --git a/identity/java/android/security/identity/IdentityCredentialException.java b/identity/java/android/security/identity/IdentityCredentialException.java
new file mode 100644
index 0000000..c811380
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredentialException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Base class for all Identity Credential exceptions.
+ */
+public class IdentityCredentialException extends Exception {
+ /**
+ * Constructs a new {@link IdentityCredentialException} exception.
+ *
+ * @param message the detail message.
+ */
+ public IdentityCredentialException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link IdentityCredentialException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public IdentityCredentialException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
new file mode 100644
index 0000000..a1dfc77
--- /dev/null
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An interface to a secure store for user identity documents.
+ *
+ * <p>This interface is deliberately fairly general and abstract. To the extent possible,
+ * specification of the message formats and semantics of communication with credential
+ * verification devices and issuing authorities (IAs) is out of scope. It provides the
+ * interface with secure storage but a credential-specific Android application will be
+ * required to implement the presentation and verification protocols and processes
+ * appropriate for the specific credential type.
+ *
+ * <p>Multiple credentials can be created. Each credential comprises:</p>
+ * <ul>
+ * <li>A document type, which is a string.</li>
+ *
+ * <li>A set of namespaces, which serve to disambiguate value names. It is recommended
+ * that namespaces be structured as reverse domain names so that IANA effectively serves
+ * as the namespace registrar.</li>
+ *
+ * <li>For each namespace, a set of name/value pairs, each with an associated set of
+ * access control profile IDs. Names are strings and values are typed and can be any
+ * value supported by <a href="http://cbor.io/">CBOR</a>.</li>
+ *
+ * <li>A set of access control profiles, each with a profile ID and a specification
+ * of the conditions which satisfy the profile's requirements.</li>
+ *
+ * <li>An asymmetric key pair which is used to authenticate the credential to the Issuing
+ * Authority, called the <em>CredentialKey</em>.</li>
+ *
+ * <li>A set of zero or more named reader authentication public keys, which are used to
+ * authenticate an authorized reader to the credential.</li>
+ *
+ * <li>A set of named signing keys, which are used to sign collections of values and session
+ * transcripts.</li>
+ * </ul>
+ *
+ * <p>Implementing support for user identity documents in secure storage requires dedicated
+ * hardware-backed support and may not always be available.
+ *
+ * <p>Two different credential stores exist - the <em>default</em> store and the
+ * <em>direct access</em> store. Most often credentials will be accessed through the default
+ * store but that requires that the Android device be powered up and fully functional.
+ * It is desirable to allow identity credential usage when the Android device's battery is too
+ * low to boot the Android operating system, so direct access to the secure hardware via NFC
+ * may allow data retrieval, if the secure hardware chooses to implement it.
+ *
+ * <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader
+ * authentication to protect data elements. The reason for this is user authentication or user
+ * approval of data release is not possible when the device is off.
+ */
+public abstract class IdentityCredentialStore {
+ IdentityCredentialStore() {}
+
+ /**
+ * Specifies that the cipher suite that will be used to secure communications between the reader
+ * is:
+ *
+ * <ul>
+ * <li>ECDHE with HKDF-SHA-256 for key agreement.</li>
+ * <li>AES-256 with GCM block mode for authenticated encryption (nonces are incremented by one
+ * for every message).</li>
+ * <li>ECDSA with SHA-256 for signing (used for signing session transcripts to defeat
+ * man-in-the-middle attacks), signing keys are not ephemeral. See {@link IdentityCredential}
+ * for details on reader and prover signing keys.</li>
+ * </ul>
+ *
+ * <p>
+ * At present this is the only supported cipher suite.
+ */
+ public static final int CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 = 1;
+
+ /**
+ * Gets the default {@link IdentityCredentialStore}.
+ *
+ * @param context the application context.
+ * @return the {@link IdentityCredentialStore} or {@code null} if the device doesn't
+ * have hardware-backed support for secure storage of user identity documents.
+ */
+ public static @Nullable IdentityCredentialStore getInstance(@NonNull Context context) {
+ return CredstoreIdentityCredentialStore.getInstance(context);
+ }
+
+ /**
+ * Gets the {@link IdentityCredentialStore} for direct access.
+ *
+ * <p>Direct access requires specialized NFC hardware and may not be supported on all
+ * devices even if default store is available. Credentials provisioned to the direct
+ * access store should <strong>always</strong> use reader authentication to protect
+ * data elements.
+ *
+ * @param context the application context.
+ * @return the {@link IdentityCredentialStore} or {@code null} if direct access is not
+ * supported on this device.
+ */
+ public static @Nullable IdentityCredentialStore getDirectAccessInstance(@NonNull
+ Context context) {
+ return CredstoreIdentityCredentialStore.getDirectAccessInstance(context);
+ }
+
+ /**
+ * Gets a list of supported document types.
+ *
+ * <p>Only the direct-access store may restrict the kind of document types that can be used for
+ * credentials. The default store always supports any document type.
+ *
+ * @return The supported document types or the empty array if any document type is supported.
+ */
+ public abstract @NonNull String[] getSupportedDocTypes();
+
+ /**
+ * Creates a new credential.
+ *
+ * @param credentialName The name used to identify the credential.
+ * @param docType The document type for the credential.
+ * @return A @{link WritableIdentityCredential} that can be used to create a new credential.
+ * @throws AlreadyPersonalizedException if a credential with the given name already exists.
+ * @throws DocTypeNotSupportedException if the given document type isn't supported by the store.
+ */
+ public abstract @NonNull WritableIdentityCredential createCredential(
+ @NonNull String credentialName, @NonNull String docType)
+ throws AlreadyPersonalizedException, DocTypeNotSupportedException;
+
+ /**
+ * Retrieve a named credential.
+ *
+ * @param credentialName the name of the credential to retrieve.
+ * @param cipherSuite the cipher suite to use for communicating with the verifier.
+ * @return The named credential, or null if not found.
+ */
+ public abstract @Nullable IdentityCredential getCredentialByName(@NonNull String credentialName,
+ @Ciphersuite int cipherSuite)
+ throws CipherSuiteNotSupportedException;
+
+ /**
+ * Delete a named credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below:
+ *
+ * <pre>
+ * ProofOfDeletion = [
+ * "ProofOfDeletion", ; tstr
+ * tstr, ; DocType
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * @param credentialName the name of the credential to delete.
+ * @return {@code null} if the credential was not found, the COSE_Sign1 data structure above
+ * if the credential was found and deleted.
+ */
+ public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
+
+ /** @hide */
+ @IntDef(value = {CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Ciphersuite {
+ }
+
+}
diff --git a/identity/java/android/security/identity/InvalidReaderSignatureException.java b/identity/java/android/security/identity/InvalidReaderSignatureException.java
new file mode 100644
index 0000000..3f70270
--- /dev/null
+++ b/identity/java/android/security/identity/InvalidReaderSignatureException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if the reader signature is invalid, or it doesn't contain a certificate chain, or if the
+ * signature failed to validate.
+ */
+public class InvalidReaderSignatureException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link InvalidReaderSignatureException} exception.
+ *
+ * @param message the detail message.
+ */
+ public InvalidReaderSignatureException(@NonNull String message) {
+ super(message);
+ }
+
+
+ /**
+ * Constructs a new {@link InvalidReaderSignatureException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public InvalidReaderSignatureException(@NonNull String message,
+ @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/InvalidRequestMessageException.java b/identity/java/android/security/identity/InvalidRequestMessageException.java
new file mode 100644
index 0000000..b0c073c
--- /dev/null
+++ b/identity/java/android/security/identity/InvalidRequestMessageException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if message with the request doesn't satisfy the requirements documented in
+ * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])}.
+ */
+public class InvalidRequestMessageException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link InvalidRequestMessageException} exception.
+ *
+ * @param message the detail message.
+ */
+ public InvalidRequestMessageException(@NonNull String message) {
+ super(message);
+ }
+
+
+ /**
+ * Constructs a new {@link InvalidRequestMessageException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public InvalidRequestMessageException(@NonNull String message,
+ @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/MessageDecryptionException.java b/identity/java/android/security/identity/MessageDecryptionException.java
new file mode 100644
index 0000000..7a6169e
--- /dev/null
+++ b/identity/java/android/security/identity/MessageDecryptionException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown when failing to decrypt a message from the reader device.
+ */
+public class MessageDecryptionException extends IdentityCredentialException {
+
+ /**
+ * Constructs a new {@link MessageDecryptionException} exception.
+ *
+ * @param message the detail message.
+ */
+ public MessageDecryptionException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link MessageDecryptionException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public MessageDecryptionException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java b/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java
new file mode 100644
index 0000000..7f40403
--- /dev/null
+++ b/identity/java/android/security/identity/NoAuthenticationKeyAvailableException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if no dynamic authentication keys are available.
+ */
+public class NoAuthenticationKeyAvailableException extends IdentityCredentialException {
+
+ /**
+ * Constructs a new {@link NoAuthenticationKeyAvailableException} exception.
+ *
+ * @param message the detail message.
+ */
+ public NoAuthenticationKeyAvailableException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link NoAuthenticationKeyAvailableException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public NoAuthenticationKeyAvailableException(@NonNull String message,
+ @NonNull Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/identity/java/android/security/identity/PersonalizationData.java b/identity/java/android/security/identity/PersonalizationData.java
new file mode 100644
index 0000000..44370a1
--- /dev/null
+++ b/identity/java/android/security/identity/PersonalizationData.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+
+/**
+ * An object that holds personalization data.
+ *
+ * This data includes access control profiles and a set of data entries and values, grouped by
+ * namespace.
+ *
+ * This is used to provision data into a {@link WritableIdentityCredential}.
+ *
+ * @see WritableIdentityCredential#personalize
+ */
+public class PersonalizationData {
+
+ private PersonalizationData() {
+ }
+
+ private LinkedList<AccessControlProfile> mProfiles = new LinkedList<>();
+
+ private LinkedHashMap<String, NamespaceData> mNamespaces = new LinkedHashMap<>();
+
+ Collection<AccessControlProfile> getAccessControlProfiles() {
+ return Collections.unmodifiableCollection(mProfiles);
+ }
+
+ Collection<String> getNamespaceNames() {
+ return Collections.unmodifiableCollection(mNamespaces.keySet());
+ }
+
+ NamespaceData getNamespaceData(String namespace) {
+ return mNamespaces.get(namespace);
+ }
+
+ static class NamespaceData {
+
+ private String mNamespace;
+ private LinkedHashMap<String, EntryData> mEntries = new LinkedHashMap<>();
+
+ private NamespaceData(String namespace) {
+ this.mNamespace = namespace;
+ }
+
+ String getNamespaceName() {
+ return mNamespace;
+ }
+
+ Collection<String> getEntryNames() {
+ return Collections.unmodifiableCollection(mEntries.keySet());
+ }
+
+ Collection<AccessControlProfileId> getAccessControlProfileIds(String name) {
+ EntryData value = mEntries.get(name);
+ if (value != null) {
+ return value.mAccessControlProfileIds;
+ }
+ return null;
+ }
+
+ byte[] getEntryValue(String name) {
+ EntryData value = mEntries.get(name);
+ if (value != null) {
+ return value.mValue;
+ }
+ return null;
+ }
+ }
+
+ private static class EntryData {
+ byte[] mValue;
+ Collection<AccessControlProfileId> mAccessControlProfileIds;
+
+ EntryData(byte[] value, Collection<AccessControlProfileId> accessControlProfileIds) {
+ this.mValue = value;
+ this.mAccessControlProfileIds = accessControlProfileIds;
+ }
+ }
+
+ /**
+ * A builder for {@link PersonalizationData}.
+ */
+ public static final class Builder {
+ private PersonalizationData mData;
+
+ /**
+ * Creates a new builder for a given namespace.
+ */
+ public Builder() {
+ this.mData = new PersonalizationData();
+ }
+
+ /**
+ * Adds a new entry to the builder.
+ *
+ * @param namespace The namespace to use, e.g. {@code org.iso.18013-5.2019}.
+ * @param name The name of the entry, e.g. {@code height}.
+ * @param accessControlProfileIds A set of access control profiles to use.
+ * @param value The value to add, in CBOR encoding.
+ * @return The builder.
+ */
+ public @NonNull Builder setEntry(@NonNull String namespace, @NonNull String name,
+ @NonNull Collection<AccessControlProfileId> accessControlProfileIds,
+ @NonNull byte[] value) {
+ NamespaceData namespaceData = mData.mNamespaces.get(namespace);
+ if (namespaceData == null) {
+ namespaceData = new NamespaceData(namespace);
+ mData.mNamespaces.put(namespace, namespaceData);
+ }
+ // TODO: validate/verify that value is proper CBOR.
+ namespaceData.mEntries.put(name, new EntryData(value, accessControlProfileIds));
+ return this;
+ }
+
+ /**
+ * Adds a new access control profile to the builder.
+ *
+ * @param profile The access control profile.
+ * @return The builder.
+ */
+ public @NonNull Builder addAccessControlProfile(@NonNull AccessControlProfile profile) {
+ mData.mProfiles.add(profile);
+ return this;
+ }
+
+ /**
+ * Creates a new {@link PersonalizationData} with all the entries added to the builder.
+ *
+ * @return A new {@link PersonalizationData} instance.
+ */
+ public @NonNull PersonalizationData build() {
+ return mData;
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
new file mode 100644
index 0000000..0982c8a
--- /dev/null
+++ b/identity/java/android/security/identity/ResultData.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.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 from a {@link IdentityCredential}.
+ */
+public abstract class ResultData {
+
+ /** Value was successfully retrieved. */
+ public static final int STATUS_OK = 0;
+
+ /** Requested entry does not exist. */
+ public static final int STATUS_NO_SUCH_ENTRY = 1;
+
+ /** Requested entry was not requested. */
+ public static final int STATUS_NOT_REQUESTED = 2;
+
+ /** Requested entry wasn't in the request message. */
+ public static final int STATUS_NOT_IN_REQUEST_MESSAGE = 3;
+
+ /** The requested entry was not retrieved because user authentication wasn't performed. */
+ public static final int STATUS_USER_AUTHENTICATION_FAILED = 4;
+
+ /** The requested entry was not retrieved because reader authentication wasn't performed. */
+ public static final int STATUS_READER_AUTHENTICATION_FAILED = 5;
+
+ /**
+ * The requested entry was not retrieved because it was configured without any access
+ * control profile.
+ */
+ public static final int STATUS_NO_ACCESS_CONTROL_PROFILES = 6;
+
+ /**
+ * @hide
+ */
+ protected ResultData() {}
+
+ /**
+ * Returns a CBOR structure containing the retrieved 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 #getMessageAuthenticationCode()} can be used to get a MAC.
+ *
+ * <p>The CBOR structure which is cryptographically authenticated is the
+ * {@code DeviceAuthentication} structure according to the following
+ * <a href="https://tools.ietf.org/html/draft-ietf-cbor-cddl-06">CDDL</a> schema:
+ *
+ * <pre>
+ * DeviceAuthentication = [
+ * "DeviceAuthentication",
+ * SessionTranscript,
+ * DocType,
+ * DeviceNameSpacesBytes
+ * ]
+ *
+ * DocType = tstr
+ *
+ * SessionTranscript = [
+ * DeviceEngagementBytes,
+ * EReaderKeyBytes
+ * ]
+ *
+ * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
+ * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+ *
+ * DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ * </pre>
+ *
+ * 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[] getAuthenticatedData();
+
+ /**
+ * Returns a message authentication code over the data returned by
+ * {@link #getAuthenticatedData}, 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.
+ *
+ * <p>If the {@code sessionTranscript} parameter passed to
+ * {@link IdentityCredential#getEntries(byte[], Map, byte[], byte[])} was {@code null}
+ * or the reader ephmeral public key was never set using
+ * {@link IdentityCredential#setReaderEphemeralPublicKey(PublicKey)}, 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[] getMessageAuthenticationCode();
+
+ /**
+ * Returns the static authentication data associated with the dynamic authentication
+ * key used to sign or MAC the data returned by {@link #getAuthenticatedData()}.
+ *
+ * @return The static authentication data associated with dynamic authentication key used to
+ * MAC the data.
+ */
+ public abstract @NonNull byte[] getStaticAuthenticationData();
+
+ /**
+ * 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.
+ */
+ public abstract @NonNull Collection<String> getNamespaceNames();
+
+ /**
+ * Get the names of all entries.
+ *
+ * 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 or {@code null} if there are no entries for the given
+ * namespace.
+ */
+ public abstract @Nullable Collection<String> getEntryNames(@NonNull String namespaceName);
+
+ /**
+ * Get the names of all entries that was successfully retrieved.
+ *
+ * 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 A collection of names or {@code null} if there are no entries for the given
+ * namespace.
+ */
+ public abstract @Nullable Collection<String> getRetrievedEntryNames(
+ @NonNull String namespaceName);
+
+ /**
+ * Gets the status of an entry.
+ *
+ * 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
+ public abstract int getStatus(@NonNull String namespaceName, @NonNull String name);
+
+ /**
+ * Gets the raw CBOR data for the value of an entry.
+ *
+ * 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.
+ */
+ public abstract @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})
+ public @interface Status {
+ }
+}
diff --git a/identity/java/android/security/identity/SessionTranscriptMismatchException.java b/identity/java/android/security/identity/SessionTranscriptMismatchException.java
new file mode 100644
index 0000000..8c24060
--- /dev/null
+++ b/identity/java/android/security/identity/SessionTranscriptMismatchException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown when trying use multiple different session transcripts in the same presentation session.
+ */
+public class SessionTranscriptMismatchException extends IdentityCredentialException {
+
+ /**
+ * Constructs a new {@link SessionTranscriptMismatchException} exception.
+ *
+ * @param message the detail message.
+ */
+ public SessionTranscriptMismatchException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link SessionTranscriptMismatchException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public SessionTranscriptMismatchException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/UnknownAuthenticationKeyException.java b/identity/java/android/security/identity/UnknownAuthenticationKeyException.java
new file mode 100644
index 0000000..f454b2c
--- /dev/null
+++ b/identity/java/android/security/identity/UnknownAuthenticationKeyException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+/**
+ * Thrown if trying to certify an unknown dynamic authentication key.
+ */
+public class UnknownAuthenticationKeyException extends IdentityCredentialException {
+ /**
+ * Constructs a new {@link UnknownAuthenticationKeyException} exception.
+ *
+ * @param message the detail message.
+ */
+ public UnknownAuthenticationKeyException(@NonNull String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new {@link UnknownAuthenticationKeyException} exception.
+ *
+ * @param message the detail message.
+ * @param cause the cause.
+ */
+ public UnknownAuthenticationKeyException(@NonNull String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/identity/java/android/security/identity/Util.java b/identity/java/android/security/identity/Util.java
new file mode 100644
index 0000000..6eefeb8
--- /dev/null
+++ b/identity/java/android/security/identity/Util.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECPoint;
+import java.util.Collection;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+
+class Util {
+ private static final String TAG = "Util";
+
+ static int[] integerCollectionToArray(Collection<Integer> collection) {
+ int[] result = new int[collection.size()];
+ int n = 0;
+ for (int item : collection) {
+ result[n++] = item;
+ }
+ return result;
+ }
+
+ static byte[] stripLeadingZeroes(byte[] value) {
+ int n = 0;
+ while (n < value.length && value[n] == 0) {
+ n++;
+ }
+ int newLen = value.length - n;
+ byte[] ret = new byte[newLen];
+ int m = 0;
+ while (n < value.length) {
+ ret[m++] = value[n++];
+ }
+ return ret;
+ }
+
+ static byte[] publicKeyEncodeUncompressedForm(PublicKey publicKey) {
+ ECPoint w = ((ECPublicKey) publicKey).getW();
+ // X and Y are always positive so for interop we remove any leading zeroes
+ // inserted by the BigInteger encoder.
+ byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
+ byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ baos.write(0x04);
+ baos.write(x);
+ baos.write(y);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("Unexpected IOException", e);
+ }
+ }
+
+ /**
+ * Computes an HKDF.
+ *
+ * This is based on https://github.com/google/tink/blob/master/java/src/main/java/com/google
+ * /crypto/tink/subtle/Hkdf.java
+ * which is also Copyright (c) Google and also licensed under the Apache 2 license.
+ *
+ * @param macAlgorithm the MAC algorithm used for computing the Hkdf. I.e., "HMACSHA1" or
+ * "HMACSHA256".
+ * @param ikm the input keying material.
+ * @param salt optional salt. A possibly non-secret random value. If no salt is
+ * provided (i.e. if
+ * salt has length 0) then an array of 0s of the same size as the hash
+ * digest is used as salt.
+ * @param info optional context and application specific information.
+ * @param size The length of the generated pseudorandom string in bytes. The maximal
+ * size is
+ * 255.DigestSize, where DigestSize is the size of the underlying HMAC.
+ * @return size pseudorandom bytes.
+ */
+ static byte[] computeHkdf(
+ String macAlgorithm, final byte[] ikm, final byte[] salt, final byte[] info, int size) {
+ Mac mac = null;
+ try {
+ mac = Mac.getInstance(macAlgorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("No such algorithm: " + macAlgorithm, e);
+ }
+ if (size > 255 * mac.getMacLength()) {
+ throw new RuntimeException("size too large");
+ }
+ try {
+ if (salt == null || salt.length == 0) {
+ // According to RFC 5869, Section 2.2 the salt is optional. If no salt is provided
+ // then HKDF uses a salt that is an array of zeros of the same length as the hash
+ // digest.
+ mac.init(new SecretKeySpec(new byte[mac.getMacLength()], macAlgorithm));
+ } else {
+ mac.init(new SecretKeySpec(salt, macAlgorithm));
+ }
+ byte[] prk = mac.doFinal(ikm);
+ byte[] result = new byte[size];
+ int ctr = 1;
+ int pos = 0;
+ mac.init(new SecretKeySpec(prk, macAlgorithm));
+ byte[] digest = new byte[0];
+ while (true) {
+ mac.update(digest);
+ mac.update(info);
+ mac.update((byte) ctr);
+ digest = mac.doFinal();
+ if (pos + digest.length < size) {
+ System.arraycopy(digest, 0, result, pos, digest.length);
+ pos += digest.length;
+ ctr++;
+ } else {
+ System.arraycopy(digest, 0, result, pos, size - pos);
+ break;
+ }
+ }
+ return result;
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Error MACing", e);
+ }
+ }
+
+}
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
new file mode 100644
index 0000000..5f575b9
--- /dev/null
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.identity;
+
+import android.annotation.NonNull;
+
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+
+/**
+ * Class used to personalize a new identity credential.
+ *
+ * <p>Credentials cannot be updated or modified after creation; any changes require deletion and
+ * re-creation.
+ *
+ * Use {@link IdentityCredentialStore#createCredential(String, String)} to create a new credential.
+ */
+public abstract class WritableIdentityCredential {
+ /**
+ * Generates and returns an X.509 certificate chain for the CredentialKey which identifies this
+ * credential to the issuing authority. The certificate contains an
+ * <a href="https://source.android.com/security/keystore/attestation">Android Keystore</a>
+ * attestation extension which describes the key and the security hardware in which it lives.
+ *
+ * <p>Additionally, the attestation extension will contain the tag TODO_IC_KEY which indicates
+ * it is an Identity Credential key (which can only sign/MAC very specific messages) and not
+ * an Android Keystore key (which can be used to sign/MAC anything).
+ *
+ * <p>The issuer <b>MUST</b> carefully examine this certificate chain including (but not
+ * limited to) checking that the root certificate is well-known, the tag TODO_IC_KEY is
+ * present, the passed in challenge is present, the device has verified boot enabled, that each
+ * certificate in the chain is signed by its successor, that none of the certificates have been
+ * revoked and so on.
+ *
+ * <p>It is not strictly necessary to use this method to provision a credential if the issuing
+ * authority doesn't care about the nature of the security hardware. If called, however, this
+ * method must be called before {@link #personalize(PersonalizationData)}.
+ *
+ * @param challenge is a byte array whose contents should be unique, fresh and provided by
+ * the issuing authority. The value provided is embedded in the attestation
+ * extension and enables the issuing authority to verify that the attestation
+ * certificate is fresh.
+ * @return the X.509 certificate for this credential's CredentialKey.
+ */
+ public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
+ @NonNull byte[] challenge);
+
+ /**
+ * Stores all of the data in the credential, with the specified access control profiles.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey with payload
+ * set to {@code ProofOfProvisioning} as defined below.
+ *
+ * <pre>
+ * ProofOfProvisioning = [
+ * "ProofOfProvisioning", ; tstr
+ * tstr, ; DocType
+ * [ * AccessControlProfile ],
+ * ProvisionedData,
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ *
+ * AccessControlProfile = {
+ * "id": uint,
+ * ? "readerCertificate" : bstr,
+ * ? (
+ * "userAuthenticationRequired" : bool,
+ * "timeoutMillis" : uint,
+ * )
+ * }
+ *
+ * ProvisionedData = {
+ * * Namespace => [ + Entry ]
+ * },
+ *
+ * Namespace = tstr
+ *
+ * Entry = {
+ * "name" : tstr,
+ * "value" : any,
+ * "accessControlProfiles" : [ * uint ],
+ * }
+ * </pre>
+ *
+ * <p>This data structure provides a guarantee to the issuer about the data which may be
+ * returned in the CBOR returned by
+ * {@link ResultData#getAuthenticatedData()} during a credential
+ * presentation.
+ *
+ * @param personalizationData The data to provision, including access control profiles
+ * and data elements and their values, grouped into namespaces.
+ * @return A COSE_Sign1 data structure, see above.
+ */
+ public abstract @NonNull byte[] personalize(
+ @NonNull PersonalizationData personalizationData);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index abfee1d..9765074 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -184,13 +184,28 @@
}
}
+ /**
+ * Connect this device.
+ *
+ * @param connectAllProfiles {@code true} to connect all profile, {@code false} otherwise.
+ *
+ * @deprecated use {@link #connect()} instead.
+ */
+ @Deprecated
public void connect(boolean connectAllProfiles) {
+ connect();
+ }
+
+ /**
+ * Connect this device.
+ */
+ public void connect() {
if (!ensurePaired()) {
return;
}
mConnectAttempted = SystemClock.elapsedRealtime();
- connectWithoutResettingTimer(connectAllProfiles);
+ connectAllEnabledProfiles();
}
public long getHiSyncId() {
@@ -211,10 +226,10 @@
void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
- connect(false);
+ connect();
}
- private void connectWithoutResettingTimer(boolean connectAllProfiles) {
+ private void connectAllEnabledProfiles() {
synchronized (mProfileLock) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
@@ -229,36 +244,7 @@
return;
}
- int preferredProfiles = 0;
- for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.accessProfileEnabled()
- : profile.isAutoConnectable()) {
- if (profile.isPreferred(mDevice)) {
- ++preferredProfiles;
- connectInt(profile);
- }
- }
- }
- if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
-
- if (preferredProfiles == 0) {
- connectAutoConnectableProfiles();
- }
- }
- }
-
- private void connectAutoConnectableProfiles() {
- if (!ensurePaired()) {
- return;
- }
-
- synchronized (mProfileLock) {
- for (LocalBluetoothProfile profile : mProfiles) {
- if (profile.isAutoConnectable()) {
- profile.setPreferred(mDevice, true);
- connectInt(profile);
- }
- }
+ mLocalAdapter.connectAllEnabledProfiles(mDevice);
}
}
@@ -625,7 +611,7 @@
*/
if (!mProfiles.isEmpty()
&& ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) {
- connectWithoutResettingTimer(false);
+ connectAllEnabledProfiles();
}
dispatchAttributesChanged();
@@ -644,7 +630,7 @@
refresh();
if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
- connect(false);
+ connect();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4e16c66..56b14c6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -116,7 +116,7 @@
final CachedBluetoothDevice cachedDevice =
((BluetoothMediaDevice) device).getCachedDevice();
if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
- cachedDevice.connect(true);
+ cachedDevice.connect();
return;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 98bb74a..4b9f6c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -127,7 +127,7 @@
mLocalMediaManager.registerCallback(mCallback);
mLocalMediaManager.connectDevice(device);
- verify(cachedDevice).connect(true);
+ verify(cachedDevice).connect();
}
@Test
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6572072..29f9e58 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -88,7 +88,6 @@
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
-import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
@@ -2835,26 +2834,11 @@
return true;
}
- // TODO: delete when direct use of registerNetworkFactory is no longer supported.
- private boolean maybeHandleNetworkFactoryMessage(Message msg) {
- switch (msg.what) {
- default:
- return false;
- case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: {
- handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid,
- /* callOnUnavailable */ true);
- break;
- }
- }
- return true;
- }
-
@Override
public void handleMessage(Message msg) {
if (!maybeHandleAsyncChannelMessage(msg)
&& !maybeHandleNetworkMonitorMessage(msg)
- && !maybeHandleNetworkAgentInfoMessage(msg)
- && !maybeHandleNetworkFactoryMessage(msg)) {
+ && !maybeHandleNetworkAgentInfoMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
@@ -5496,7 +5480,10 @@
// changes that would conflict throughout the automerger graph. Having this method temporarily
// helps with the process of going through with all these dependent changes across the entire
// tree.
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ /**
+ * Register a new agent. {@see #registerNetworkAgent} below.
+ */
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc) {
return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
@@ -5517,8 +5504,9 @@
* {@link NetworkAgentInfo#getCurrentScore}.
* @param networkMisc metadata about the network. This is never updated.
* @param providerId the ID of the provider owning this NetworkAgent.
+ * @return the network created for this agent.
*/
- public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+ public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc, int providerId) {
enforceNetworkFactoryPermission();
@@ -5551,7 +5539,7 @@
// If the network disconnects or sends any other event before that, messages are deferred by
// NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
// registration.
- return nai.network.netId;
+ return nai.network;
}
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
@@ -5803,6 +5791,19 @@
return INetd.PERMISSION_NONE;
}
+ private void updateNetworkPermissions(@NonNull final NetworkAgentInfo nai,
+ @NonNull final NetworkCapabilities newNc) {
+ final int oldPermission = getNetworkPermission(nai.networkCapabilities);
+ final int newPermission = getNetworkPermission(newNc);
+ if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
+ try {
+ mNMS.setNetworkPermission(nai.network.netId, newPermission);
+ } catch (RemoteException e) {
+ loge("Exception in setNetworkPermission: " + e);
+ }
+ }
+ }
+
/**
* Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
@@ -5874,21 +5875,11 @@
* @param nai the network having its capabilities updated.
* @param nc the new network capabilities.
*/
- private void updateCapabilities(int oldScore, NetworkAgentInfo nai, NetworkCapabilities nc) {
+ private void updateCapabilities(final int oldScore, @NonNull final NetworkAgentInfo nai,
+ @NonNull final NetworkCapabilities nc) {
NetworkCapabilities newNc = mixInCapabilities(nai, nc);
-
if (Objects.equals(nai.networkCapabilities, newNc)) return;
-
- final int oldPermission = getNetworkPermission(nai.networkCapabilities);
- final int newPermission = getNetworkPermission(newNc);
- if (oldPermission != newPermission && nai.created && !nai.isVPN()) {
- try {
- mNMS.setNetworkPermission(nai.network.netId, newPermission);
- } catch (RemoteException e) {
- loge("Exception in setNetworkPermission: " + e);
- }
- }
-
+ updateNetworkPermissions(nai, newNc);
final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
updateUids(nai, prevNc, newNc);
@@ -6253,6 +6244,30 @@
}
}
+ // An accumulator class to gather the list of changes that result from a rematch.
+ // TODO : enrich to represent an entire set of changes to apply.
+ private static class NetworkReassignment {
+ static class NetworkBgStatePair {
+ @NonNull final NetworkAgentInfo mNetwork;
+ final boolean mOldBackground;
+ NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
+ final boolean oldBackground) {
+ mNetwork = network;
+ mOldBackground = oldBackground;
+ }
+ }
+
+ @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
+
+ @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
+ return mRematchedNetworks;
+ }
+
+ void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
+ mRematchedNetworks.add(network);
+ }
+ }
+
private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
@NonNull final NetworkAgentInfo newNetwork) {
final int score = newNetwork.getCurrentScore();
@@ -6298,8 +6313,8 @@
// needed. A network is needed if it is the best network for
// one or more NetworkRequests, or if it is a VPN.
//
- // - Tears down newNetwork if it just became validated
- // but turns out to be unneeded.
+ // - Writes into the passed reassignment object all changes that should be done for
+ // rematching this network with all requests, to be applied later.
//
// NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
// it does not remove NetworkRequests that other Networks could better satisfy.
@@ -6307,15 +6322,22 @@
// This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
// as it performs better by a factor of the number of Networks.
//
+ // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
+ // it's unidiomatic Java and it's hard to read.
+ //
+ // @param changes a currently-building list of changes to write to
// @param newNetwork is the network to be matched against NetworkRequests.
// @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
- private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, long now) {
+ private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
+ @NonNull final NetworkAgentInfo newNetwork, final long now) {
ensureRunningOnConnectivityServiceThread();
if (!newNetwork.everConnected) return;
boolean isNewDefault = false;
NetworkAgentInfo oldDefaultNetwork = null;
- final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork();
+ changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
+ newNetwork.isBackgroundNetwork()));
+
final int score = newNetwork.getCurrentScore();
if (VDBG || DDBG) log("rematching " + newNetwork.name());
@@ -6418,39 +6440,12 @@
if (newNetwork.getCurrentScore() != score) {
Slog.wtf(TAG, String.format(
"BUG: %s changed score during rematch: %d -> %d",
- newNetwork.name(), score, newNetwork.getCurrentScore()));
+ newNetwork.name(), score, newNetwork.getCurrentScore()));
}
// Notify requested networks are available after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
-
- // Finally, process listen requests and update capabilities if the background state has
- // changed for this network. For consistency with previous behavior, send onLost callbacks
- // before onAvailable.
- processNewlyLostListenRequests(newNetwork);
-
- // Maybe the network changed background states. Update its capabilities.
- final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork();
- if (backgroundChanged) {
- final NetworkCapabilities newNc = mixInCapabilities(newNetwork,
- newNetwork.networkCapabilities);
-
- final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
- final int newPermission = getNetworkPermission(newNc);
- if (oldPermission != newPermission) {
- try {
- mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
- } catch (RemoteException e) {
- loge("Exception in setNetworkPermission: " + e);
- }
- }
-
- newNetwork.getAndSetNetworkCapabilities(newNc);
- notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
- processNewlySatisfiedListenRequests(newNetwork);
}
/**
@@ -6472,12 +6467,24 @@
// scoring network and then a higher scoring network, which could produce multiple
// callbacks.
Arrays.sort(nais);
+ final NetworkReassignment changes = new NetworkReassignment();
for (final NetworkAgentInfo nai : nais) {
- rematchNetworkAndRequests(nai, now);
+ rematchNetworkAndRequests(changes, nai, now);
}
final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
+ for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
+ // Process listen requests and update capabilities if the background state has
+ // changed for this network. For consistency with previous behavior, send onLost
+ // callbacks before onAvailable.
+ processNewlyLostListenRequests(event.mNetwork);
+ if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
+ applyBackgroundChangeForRematch(event.mNetwork);
+ }
+ processNewlySatisfiedListenRequests(event.mNetwork);
+ }
+
for (final NetworkAgentInfo nai : nais) {
// Rematching may have altered the linger state of some networks, so update all linger
// timers. updateLingerState reads the state from the network agent and does nothing
@@ -6509,6 +6516,24 @@
}
}
+ /**
+ * Apply a change in background state resulting from rematching networks with requests.
+ *
+ * During rematch, a network may change background states by starting to satisfy or stopping
+ * to satisfy a foreground request. Listens don't count for this. When a network changes
+ * background states, its capabilities need to be updated and callbacks fired for the
+ * capability change.
+ *
+ * @param nai The network that changed background states
+ */
+ private void applyBackgroundChangeForRematch(@NonNull final NetworkAgentInfo nai) {
+ final NetworkCapabilities newNc = mixInCapabilities(nai, nai.networkCapabilities);
+ if (Objects.equals(nai.networkCapabilities, newNc)) return;
+ updateNetworkPermissions(nai, newNc);
+ nai.getAndSetNetworkCapabilities(newNc);
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
+
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
@Nullable final NetworkAgentInfo oldDefaultNetwork,
@Nullable final NetworkAgentInfo newDefaultNetwork,
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index 1ff455ea..c34dd98 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,15 +16,273 @@
package com.android.server;
-import android.os.IBinder;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.timedetector.NetworkTimeSuggestion;
+import android.app.timedetector.TimeDetector;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.NtpTrustedTime;
+import android.util.TimeUtils;
+
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
/**
- * An interface for NetworkTimeUpdateService implementations. Eventually part or all of this service
- * will be subsumed into {@link com.android.server.timedetector.TimeDetectorService}. In the
- * meantime this interface allows Android to use either the old or new implementation.
+ * Monitors the network time. If looking up the network time fails for some reason, it tries a few
+ * times with a short interval and then resets to checking on longer intervals.
+ *
+ * <p>When available, the time is always suggested to the {@link
+ * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
+ * system clock, depending on user settings and what other signals are available.
*/
-public interface NetworkTimeUpdateService extends IBinder {
+public class NetworkTimeUpdateService extends Binder {
+
+ private static final String TAG = "NetworkTimeUpdateService";
+ private static final boolean DBG = false;
+
+ private static final int EVENT_AUTO_TIME_ENABLED = 1;
+ private static final int EVENT_POLL_NETWORK_TIME = 2;
+ private static final int EVENT_NETWORK_CHANGED = 3;
+
+ private static final String ACTION_POLL =
+ "com.android.server.NetworkTimeUpdateService.action.POLL";
+
+ private static final int POLL_REQUEST = 0;
+
+ private Network mDefaultNetwork = null;
+
+ private final Context mContext;
+ private final NtpTrustedTime mTime;
+ private final AlarmManager mAlarmManager;
+ private final TimeDetector mTimeDetector;
+ private final ConnectivityManager mCM;
+ private final PendingIntent mPendingPollIntent;
+ private final PowerManager.WakeLock mWakeLock;
+
+ // NTP lookup is done on this thread and handler
+ private Handler mHandler;
+ private AutoTimeSettingObserver mAutoTimeSettingObserver;
+ private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
+
+ // Normal polling frequency
+ private final long mPollingIntervalMs;
+ // Try-again polling interval, in case the network request failed
+ private final long mPollingIntervalShorterMs;
+ // Number of times to try again
+ private final int mTryAgainTimesMax;
+ // Keeps track of how many quick attempts were made to fetch NTP time.
+ // During bootup, the network may not have been up yet, or it's taking time for the
+ // connection to happen.
+ private int mTryAgainCounter;
+
+ public NetworkTimeUpdateService(Context context) {
+ mContext = context;
+ mTime = NtpTrustedTime.getInstance(context);
+ mAlarmManager = mContext.getSystemService(AlarmManager.class);
+ mTimeDetector = mContext.getSystemService(TimeDetector.class);
+ mCM = mContext.getSystemService(ConnectivityManager.class);
+
+ Intent pollIntent = new Intent(ACTION_POLL, null);
+ mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
+
+ mPollingIntervalMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingInterval);
+ mPollingIntervalShorterMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpPollingIntervalShorter);
+ mTryAgainTimesMax = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_ntpRetry);
+
+ mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
/** Initialize the receivers and initiate the first NTP request */
- void systemRunning();
+ public void systemRunning() {
+ registerForAlarms();
+
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new MyHandler(thread.getLooper());
+ mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
+ mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
+
+ mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
+ EVENT_AUTO_TIME_ENABLED);
+ mAutoTimeSettingObserver.observe();
+ }
+
+ private void registerForAlarms() {
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
+ }
+ }, new IntentFilter(ACTION_POLL));
+ }
+
+ private void onPollNetworkTime(int event) {
+ // If we don't have any default network, don't bother.
+ if (mDefaultNetwork == null) return;
+ mWakeLock.acquire();
+ try {
+ onPollNetworkTimeUnderWakeLock(event);
+ } finally {
+ mWakeLock.release();
+ }
+ }
+
+ private void onPollNetworkTimeUnderWakeLock(int event) {
+ // Force an NTP fix when outdated
+ NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
+ if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
+ if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
+ mTime.forceRefresh();
+ cachedNtpResult = mTime.getCachedTimeResult();
+ }
+
+ if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
+ // Obtained fresh fix; schedule next normal update
+ resetAlarm(mPollingIntervalMs);
+
+ // Suggest the time to the time detector. It may choose use it to set the system clock.
+ TimestampedValue<Long> timeSignal = new TimestampedValue<>(
+ cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
+ NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
+ timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
+ mTimeDetector.suggestNetworkTime(timeSuggestion);
+ } else {
+ // No fresh fix; schedule retry
+ mTryAgainCounter++;
+ if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
+ resetAlarm(mPollingIntervalShorterMs);
+ } else {
+ // Try much later
+ mTryAgainCounter = 0;
+ resetAlarm(mPollingIntervalMs);
+ }
+ }
+ }
+
+ /**
+ * Cancel old alarm and starts a new one for the specified interval.
+ *
+ * @param interval when to trigger the alarm, starting from now.
+ */
+ private void resetAlarm(long interval) {
+ mAlarmManager.cancel(mPendingPollIntent);
+ long now = SystemClock.elapsedRealtime();
+ long next = now + interval;
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
+ }
+
+ /** Handler to do the network accesses on */
+ private class MyHandler extends Handler {
+
+ MyHandler(Looper l) {
+ super(l);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_AUTO_TIME_ENABLED:
+ case EVENT_POLL_NETWORK_TIME:
+ case EVENT_NETWORK_CHANGED:
+ onPollNetworkTime(msg.what);
+ break;
+ }
+ }
+ }
+
+ private class NetworkTimeUpdateCallback extends NetworkCallback {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, String.format("New default network %s; checking time.", network));
+ mDefaultNetwork = network;
+ // Running on mHandler so invoke directly.
+ onPollNetworkTime(EVENT_NETWORK_CHANGED);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
+ }
+ }
+
+ /**
+ * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
+ * is enabled.
+ */
+ private static class AutoTimeSettingObserver extends ContentObserver {
+
+ private final Context mContext;
+ private final int mMsg;
+ private final Handler mHandler;
+
+ AutoTimeSettingObserver(Context context, Handler handler, int msg) {
+ super(handler);
+ mContext = context;
+ mHandler = handler;
+ mMsg = msg;
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
+ false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (isAutomaticTimeEnabled()) {
+ mHandler.obtainMessage(mMsg).sendToTarget();
+ }
+ }
+
+ /**
+ * Checks if the user prefers to automatically set the time.
+ */
+ private boolean isAutomaticTimeEnabled() {
+ ContentResolver resolver = mContext.getContentResolver();
+ return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.print("PollingIntervalMs: ");
+ TimeUtils.formatDuration(mPollingIntervalMs, pw);
+ pw.print("\nPollingIntervalShorterMs: ");
+ TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
+ pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
+ pw.println("\nTryAgainCounter: " + mTryAgainCounter);
+ NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
+ pw.println("NTP cache result: " + ntpResult);
+ if (ntpResult != null) {
+ pw.println("NTP result age: " + ntpResult.getAgeMillis());
+ }
+ pw.println();
+ }
}
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
deleted file mode 100644
index 7894788..0000000
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2010 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.AlarmManager;
-import android.app.PendingIntent;
-import android.app.timedetector.NetworkTimeSuggestion;
-import android.app.timedetector.TimeDetector;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.ContentObserver;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.os.TimestampedValue;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.NtpTrustedTime;
-import android.util.TimeUtils;
-
-import com.android.internal.util.DumpUtils;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Monitors the network time. If looking up the network time fails for some reason, it tries a few
- * times with a short interval and then resets to checking on longer intervals.
- *
- * <p>When available, the time is always suggested to the {@link
- * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
- * system clock, depending on user settings and what other signals are available.
- */
-public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
-
- private static final String TAG = "NetworkTimeUpdateService";
- private static final boolean DBG = false;
-
- private static final int EVENT_AUTO_TIME_ENABLED = 1;
- private static final int EVENT_POLL_NETWORK_TIME = 2;
- private static final int EVENT_NETWORK_CHANGED = 3;
-
- private static final String ACTION_POLL =
- "com.android.server.NetworkTimeUpdateService.action.POLL";
-
- private static final int POLL_REQUEST = 0;
-
- private Network mDefaultNetwork = null;
-
- private final Context mContext;
- private final NtpTrustedTime mTime;
- private final AlarmManager mAlarmManager;
- private final TimeDetector mTimeDetector;
- private final ConnectivityManager mCM;
- private final PendingIntent mPendingPollIntent;
- private final PowerManager.WakeLock mWakeLock;
-
- // NTP lookup is done on this thread and handler
- private Handler mHandler;
- private AutoTimeSettingObserver mAutoTimeSettingObserver;
- private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
-
- // Normal polling frequency
- private final long mPollingIntervalMs;
- // Try-again polling interval, in case the network request failed
- private final long mPollingIntervalShorterMs;
- // Number of times to try again
- private final int mTryAgainTimesMax;
- // Keeps track of how many quick attempts were made to fetch NTP time.
- // During bootup, the network may not have been up yet, or it's taking time for the
- // connection to happen.
- private int mTryAgainCounter;
-
- public NetworkTimeUpdateServiceImpl(Context context) {
- mContext = context;
- mTime = NtpTrustedTime.getInstance(context);
- mAlarmManager = mContext.getSystemService(AlarmManager.class);
- mTimeDetector = mContext.getSystemService(TimeDetector.class);
- mCM = mContext.getSystemService(ConnectivityManager.class);
-
- Intent pollIntent = new Intent(ACTION_POLL, null);
- mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
-
- mPollingIntervalMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingInterval);
- mPollingIntervalShorterMs = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpPollingIntervalShorter);
- mTryAgainTimesMax = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_ntpRetry);
-
- mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, TAG);
- }
-
- @Override
- public void systemRunning() {
- registerForAlarms();
-
- HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- mHandler = new MyHandler(thread.getLooper());
- mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
- mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
-
- mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
- EVENT_AUTO_TIME_ENABLED);
- mAutoTimeSettingObserver.observe();
- }
-
- private void registerForAlarms() {
- mContext.registerReceiver(
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
- }
- }, new IntentFilter(ACTION_POLL));
- }
-
- private void onPollNetworkTime(int event) {
- // If we don't have any default network, don't bother.
- if (mDefaultNetwork == null) return;
- mWakeLock.acquire();
- try {
- onPollNetworkTimeUnderWakeLock(event);
- } finally {
- mWakeLock.release();
- }
- }
-
- private void onPollNetworkTimeUnderWakeLock(int event) {
- // Force an NTP fix when outdated
- NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
- if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
- if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
- mTime.forceRefresh();
- cachedNtpResult = mTime.getCachedTimeResult();
- }
-
- if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
- // Obtained fresh fix; schedule next normal update
- resetAlarm(mPollingIntervalMs);
-
- // Suggest the time to the time detector. It may choose use it to set the system clock.
- TimestampedValue<Long> timeSignal = new TimestampedValue<>(
- cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
- NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
- timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
- mTimeDetector.suggestNetworkTime(timeSuggestion);
- } else {
- // No fresh fix; schedule retry
- mTryAgainCounter++;
- if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
- resetAlarm(mPollingIntervalShorterMs);
- } else {
- // Try much later
- mTryAgainCounter = 0;
- resetAlarm(mPollingIntervalMs);
- }
- }
- }
-
- /**
- * Cancel old alarm and starts a new one for the specified interval.
- *
- * @param interval when to trigger the alarm, starting from now.
- */
- private void resetAlarm(long interval) {
- mAlarmManager.cancel(mPendingPollIntent);
- long now = SystemClock.elapsedRealtime();
- long next = now + interval;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
- }
-
- /** Handler to do the network accesses on */
- private class MyHandler extends Handler {
-
- public MyHandler(Looper l) {
- super(l);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case EVENT_AUTO_TIME_ENABLED:
- case EVENT_POLL_NETWORK_TIME:
- case EVENT_NETWORK_CHANGED:
- onPollNetworkTime(msg.what);
- break;
- }
- }
- }
-
- private class NetworkTimeUpdateCallback extends NetworkCallback {
- @Override
- public void onAvailable(Network network) {
- Log.d(TAG, String.format("New default network %s; checking time.", network));
- mDefaultNetwork = network;
- // Running on mHandler so invoke directly.
- onPollNetworkTime(EVENT_NETWORK_CHANGED);
- }
-
- @Override
- public void onLost(Network network) {
- if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
- }
- }
-
- /**
- * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
- * is enabled.
- */
- private static class AutoTimeSettingObserver extends ContentObserver {
-
- private final Context mContext;
- private final int mMsg;
- private final Handler mHandler;
-
- AutoTimeSettingObserver(Context context, Handler handler, int msg) {
- super(handler);
- mContext = context;
- mHandler = handler;
- mMsg = msg;
- }
-
- void observe() {
- ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
- false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- if (isAutomaticTimeEnabled()) {
- mHandler.obtainMessage(mMsg).sendToTarget();
- }
- }
-
- /**
- * Checks if the user prefers to automatically set the time.
- */
- private boolean isAutomaticTimeEnabled() {
- ContentResolver resolver = mContext.getContentResolver();
- return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- pw.print("PollingIntervalMs: ");
- TimeUtils.formatDuration(mPollingIntervalMs, pw);
- pw.print("\nPollingIntervalShorterMs: ");
- TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
- pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
- pw.println("\nTryAgainCounter: " + mTryAgainCounter);
- NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
- pw.println("NTP cache result: " + ntpResult);
- if (ntpResult != null) {
- pw.println("NTP result age: " + ntpResult.getAgeMillis());
- }
- pw.println();
- }
-}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 8b436c5..62743aa 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -287,7 +287,10 @@
static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_PRECISE_CALL_STATE
- | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+ | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+ | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+ | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES;
static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -2397,8 +2400,14 @@
}
if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+ // check if calling app has either permission READ_PRECISE_PHONE_STATE
+ // or with carrier privileges
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+ } catch (SecurityException se) {
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, subId, message);
+ }
}
if ((events & READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK) != 0) {
@@ -2416,16 +2425,6 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
}
- if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
- }
-
- if ((events & PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
- }
-
if ((events & PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -2436,11 +2435,6 @@
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
}
- if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
- }
-
return true;
}
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index d19d2dd..a7e36b2 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -219,7 +219,7 @@
// Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up
// resources, even for binder death or unwanted calls.
synchronized (mTestNetworkTracker) {
- mTestNetworkTracker.remove(netId);
+ mTestNetworkTracker.remove(network.netId);
}
}
}
@@ -338,7 +338,7 @@
callingUid,
binder);
- mTestNetworkTracker.put(agent.netId, agent);
+ mTestNetworkTracker.put(agent.network.netId, agent);
}
} catch (SocketException e) {
throw new UncheckedIOException(e);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a46ada8..08e73ef 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -848,7 +848,7 @@
}
public int getNetId() {
- return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
+ return mNetworkAgent != null ? mNetworkAgent.network.netId : NETID_UNSET;
}
private LinkProperties makeLinkProperties() {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c60fed0..9760185 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -169,6 +169,7 @@
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IDeviceIdleController;
import android.os.INetworkManagementService;
@@ -876,7 +877,8 @@
// Listen for subscriber changes
mContext.getSystemService(SubscriptionManager.class).addOnSubscriptionsChangedListener(
- new OnSubscriptionsChangedListener(mHandler.getLooper()) {
+ new HandlerExecutor(mHandler),
+ new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
updateNetworksInternal();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e991a91..609a415 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1159,7 +1159,7 @@
}
abandonSession = false;
- if (!params.sessionParams.isStaged || !params.waitForStagedSessionReady) {
+ if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) {
pw.println("Success");
return 0;
}
@@ -1197,7 +1197,7 @@
+ si.getStagedSessionErrorMessage() + "]");
return 1;
}
- pw.println("Success");
+ pw.println("Success. Reboot device to apply staged session");
return 0;
} finally {
if (abandonSession) {
@@ -2487,7 +2487,7 @@
SessionParams sessionParams;
String installerPackageName;
int userId = UserHandle.USER_ALL;
- boolean waitForStagedSessionReady = false;
+ boolean mWaitForStagedSessionReady = true;
long timeoutMs = DEFAULT_WAIT_MS;
}
@@ -2615,13 +2615,16 @@
sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
break;
case "--wait":
- params.waitForStagedSessionReady = true;
+ params.mWaitForStagedSessionReady = true;
try {
params.timeoutMs = Long.parseLong(peekNextArg());
getNextArg();
} catch (NumberFormatException ignore) {
}
break;
+ case "--no-wait":
+ params.mWaitForStagedSessionReady = false;
+ break;
default:
throw new IllegalArgumentException("Unknown option " + opt);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b5b21f4..c566341 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1695,7 +1695,7 @@
if (!isWatch && !disableNetworkTime) {
traceBeginAndSlog("StartNetworkTimeUpdateService");
try {
- networkTimeUpdater = new NetworkTimeUpdateServiceImpl(context);
+ networkTimeUpdater = new NetworkTimeUpdateService(context);
ServiceManager.addService("network_time_update_service", networkTimeUpdater);
} catch (Throwable e) {
reportWtf("starting NetworkTimeUpdate service", e);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 61e67be..d863090 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3111,7 +3111,6 @@
* 1 - yes. This is default.
* @hide
*/
- // TODO(b/119567985): name this key properly
public static final String KEY_SUPL_ES_STRING = KEY_PREFIX + "supl_es";
/**
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 39af2e7..cb3f0f9 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -30,10 +30,7 @@
/**
* This class defines an IMS-related exception that has been thrown while interacting with a
* device or carrier provided ImsService implementation.
- * @hide
*/
-@SystemApi
-@TestApi
public final class ImsException extends Exception {
/**
@@ -83,7 +80,10 @@
/**
* A new {@link ImsException} with an unspecified {@link ImsErrorCode} code.
* @param message an optional message to detail the error condition more specifically.
+ * @hide
*/
+ @SystemApi
+ @TestApi
public ImsException(@Nullable String message) {
super(getMessage(message, CODE_ERROR_UNSPECIFIED));
}
@@ -91,7 +91,10 @@
/**
* A new {@link ImsException} that includes an {@link ImsErrorCode} error code.
* @param message an optional message to detail the error condition more specifically.
+ * @hide
*/
+ @SystemApi
+ @TestApi
public ImsException(@Nullable String message, @ImsErrorCode int code) {
super(getMessage(message, code));
mCode = code;
@@ -102,7 +105,10 @@
* {@link Throwable} that contains the original error that was thrown to lead to this Exception.
* @param message an optional message to detail the error condition more specifically.
* @param cause the {@link Throwable} that caused this {@link ImsException} to be created.
+ * @hide
*/
+ @SystemApi
+ @TestApi
public ImsException(@Nullable String message, @ImsErrorCode int code,
@Nullable Throwable cause) {
super(getMessage(message, code), cause);
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 5fd0af5..c66672f 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -23,6 +23,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
@@ -56,10 +58,7 @@
* associated subscription.
*
* @see #createForSubscriptionId(int)
- * @hide
*/
-@SystemApi
-@TestApi
public class ImsMmTelManager implements RegistrationManager {
/**
@@ -94,9 +93,11 @@
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
* @see #unregisterImsRegistrationCallback(RegistrationCallback)
* @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
+ * @hide
*/
// Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
@Deprecated
+ @SystemApi @TestApi
public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
/**
@@ -141,7 +142,7 @@
/**
* Receives IMS capability status updates from the ImsService. This information is also
- * available via the {@link #isAvailable(int, int)} method below.
+ * available via the {@see #isAvailable(int, int)} method below.
*
* @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
* @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
@@ -190,7 +191,7 @@
* If unavailable, the feature is not able to support the unavailable capability at this
* time.
*
- * This information can also be queried using the {@link #isAvailable(int, int)} API.
+ * This information can also be queried using the {@see #isAvailable(int, int)} API.
*
* @param capabilities The new availability of the capabilities.
*/
@@ -218,8 +219,20 @@
*
* @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
* @throws IllegalArgumentException if the subscription is invalid.
+ *
*/
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE
+ })
+ @SuppressLint("ManagerLookup")
public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) {
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid subscription ID");
@@ -258,8 +271,10 @@
* reason.
* @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor,
* RegistrationManager.RegistrationCallback)} instead.
+ * @hide
*/
@Deprecated
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationCallback c) throws ImsException {
@@ -284,9 +299,20 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * {@inheritDoc}
+ *
+ */
@Override
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationManager.RegistrationCallback c) throws ImsException {
if (c == null) {
@@ -317,8 +343,10 @@
* @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
* @deprecated Use {@link #unregisterImsRegistrationCallback(
* RegistrationManager.RegistrationCallback)}.
+ * @hide
*/
@Deprecated
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
if (c == null) {
@@ -331,9 +359,20 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ *
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
+ *{@inheritDoc}
+ */
@Override
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public void unregisterImsRegistrationCallback(
@NonNull RegistrationManager.RegistrationCallback c) {
if (c == null) {
@@ -346,9 +385,13 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi @TestApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
if (stateCallback == null) {
@@ -369,9 +412,19 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
+ *{@inheritDoc}
+ */
@Override
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@NonNull @AccessNetworkConstants.TransportType
Consumer<Integer> transportTypeCallback) {
@@ -397,12 +450,25 @@
/**
* Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
* availability updates for the subscription specified in
- * {@link #createForSubscriptionId(int)}. The method {@link #isAvailable(int, int)}
+ * {@link #createForSubscriptionId(int)}. The method {@see #isAvailable(int, int)}
* can also be used to query this information at any time.
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
* {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
*
* When the callback is registered, it will initiate the callback c to be called with the
* current capabilities.
@@ -418,7 +484,10 @@
* the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
* reason.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull CapabilityCallback c) throws ImsException {
if (c == null) {
@@ -450,10 +519,27 @@
* When the subscription associated with this callback is removed (SIM removed, ESIM swap,
* etc...), this callback will automatically be removed. If this method is called for an
* inactive subscription, it will result in a no-op.
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @param c The MmTel {@link CapabilityCallback} to be removed.
* @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
@@ -475,6 +561,19 @@
* <p>
* Note: If the carrier configuration for advanced calling is not editable or hidden, this
* method will always return the default value.
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
*
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
* @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
@@ -486,7 +585,10 @@
* active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user's setting for advanced calling is enabled, false otherwise.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isAdvancedCallingSettingEnabled() {
try {
return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
@@ -524,8 +626,10 @@
* @see #isAdvancedCallingSettingEnabled()
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
+ * @hide
*/
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi @TestApi
public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
try {
getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
@@ -556,13 +660,15 @@
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* @param capability The IMS MmTel capability to query, can be one of the following:
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
* @return {@code true} if the MmTel IMS capability is capable for this subscription, false
* otherwise.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi @TestApi
public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
try {
@@ -583,12 +689,14 @@
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* @param capability The IMS MmTel capability to query, can be one of the following:
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
+ * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
* {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
* @return {@code true} if the MmTel IMS capability is available for this subscription, false
* otherwise.
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
@@ -613,7 +721,9 @@
* capability is supported on this carrier network for the transport specified.
* @throws ImsException if the subscription is no longer valid or the IMS service is not
* available.
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
@AccessNetworkConstants.TransportType int transportType,
@@ -642,12 +752,32 @@
/**
* The user's setting for whether or not they have enabled the "Video Calling" setting.
*
+ * <p>
+ * Note: If the carrier configuration for advanced calling is not editable or hidden, this
+ * method will always return the default value.
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user’s “Video Calling” setting is currently enabled.
* @see #setVtSettingEnabled(boolean)
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
public boolean isVtSettingEnabled() {
try {
return getITelephony().isVtSettingEnabled(mSubId);
@@ -669,7 +799,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #isVtSettingEnabled()
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVtSettingEnabled(boolean isEnabled) {
try {
@@ -689,11 +821,28 @@
/**
* @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
*
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiSettingEnabled(boolean)
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isVoWiFiSettingEnabled() {
try {
return getITelephony().isVoWiFiSettingEnabled(mSubId);
@@ -716,7 +865,9 @@
* active (SIM is not inserted, ESIM inactive) or invalid.
* @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
* @see #isVoWiFiSettingEnabled()
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiSettingEnabled(boolean isEnabled) {
try {
@@ -736,13 +887,30 @@
/**
* Returns the user's voice over WiFi roaming setting associated with the current subscription.
*
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @return true if the user's setting for Voice over WiFi while roaming is enabled, false
* if disabled.
* @see #setVoWiFiRoamingSettingEnabled(boolean)
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isVoWiFiRoamingSettingEnabled() {
try {
return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
@@ -766,7 +934,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #isVoWiFiRoamingSettingEnabled()
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
try {
@@ -796,7 +966,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiSettingEnabled(boolean)
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
try {
@@ -816,6 +988,20 @@
/**
* Returns the user's voice over WiFi Roaming mode setting associated with the device.
*
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @return The Voice over WiFi Mode preference set by the user, which can be one of the
@@ -825,7 +1011,10 @@
* - {@link #WIFI_MODE_WIFI_PREFERRED}
* @see #setVoWiFiSettingEnabled(boolean)
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public @WiFiCallingMode int getVoWiFiModeSetting() {
try {
return getITelephony().getVoWiFiModeSetting(mSubId);
@@ -851,7 +1040,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #getVoWiFiModeSetting()
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
try {
@@ -880,7 +1071,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #setVoWiFiRoamingSettingEnabled(boolean)
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
@@ -909,7 +1102,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see #getVoWiFiRoamingModeSetting()
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
try {
@@ -936,7 +1131,9 @@
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @param isEnabled if true RTT should be enabled during calls made on this subscription.
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
public void setRttCapabilitySetting(boolean isEnabled) {
try {
@@ -956,12 +1153,29 @@
/**
* @return true if TTY over VoLTE is supported
*
+ * <p>This API requires one of the following:
+ * <ul>
+ * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
+ * <li>If the caller is the device or profile owner, the caller holds the
+ * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
+ * <li>The caller has carrier privileges (see
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
+ * active subscription.</li>
+ * <li>The caller is the default SMS app for the device.</li>
+ * </ul>
+ * <p>The profile owner is an app that owns a managed profile on the device; for more details
+ * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Access by profile owners is deprecated and will be removed in a future release.
+ *
* @throws IllegalArgumentException if the subscription associated with this operation is not
* active (SIM is not inserted, ESIM inactive) or invalid.
* @see android.telecom.TelecomManager#getCurrentTtyMode
* @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE})
public boolean isTtyOverVolteEnabled() {
try {
return getITelephony().isTtyOverVolteEnabled(mSubId);
@@ -988,7 +1202,9 @@
* specified when the service state has been retrieved from the IMS service.
* @throws ImsException if the IMS service associated with this subscription is not available or
* the IMS service is not available.
+ * @hide
*/
+ @SystemApi @TestApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
@NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 99bb259..a1f6b78 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -22,8 +22,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.Binder;
import android.telephony.AccessNetworkConstants;
@@ -41,10 +39,7 @@
/**
* Manages IMS Service registration state for associated {@link ImsFeature}s.
- * @hide
*/
-@SystemApi
-@TestApi
public interface RegistrationManager {
/**
@@ -139,7 +134,6 @@
getAccessType(imsRadioTech), info)));
}
- @Override
public void onSubscriberAssociatedUriChanged(Uri[] uris) {
if (mLocalCallback == null) return;
@@ -225,7 +219,11 @@
/**
* Registers a {@link RegistrationCallback} with the system. Use
- * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ * @param c A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * {@see SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
* events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
*
* When the callback is registered, it will initiate the callback c to be called with the
@@ -275,10 +273,10 @@
* Gets the Transport Type associated with the current IMS registration.
* @param executor The {@link Executor} that will be used to call the transportTypeCallback.
* @param transportTypeCallback The transport type associated with the current IMS registration,
- * which will be one of following:
- * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
- * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
- * {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
+ * which will be one of following:
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
void getRegistrationTransportType(
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index f14270f..5d102cb4 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -60,15 +60,21 @@
* This feature supports emergency calling over MMTEL. If defined, the framework will try to
* place an emergency call over IMS first. If it is not defined, the framework will only use
* CSFB for emergency calling.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int FEATURE_EMERGENCY_MMTEL = 0;
/**
* This feature supports the MMTEL feature.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int FEATURE_MMTEL = 1;
/**
* This feature supports the RCS feature.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int FEATURE_RCS = 2;
/**
* Total number of features defined
@@ -116,18 +122,24 @@
* This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will
* remove all bindings back to the framework. Any attempt to communicate with the framework
* during this time will result in an {@link IllegalStateException}.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int STATE_UNAVAILABLE = 0;
/**
* This {@link ImsFeature} state is initializing and should not be communicated with. This will
* remove all bindings back to the framework. Any attempt to communicate with the framework
* during this time will result in an {@link IllegalStateException}.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int STATE_INITIALIZING = 1;
/**
* This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods
- * until {@link #onFeatureReady()} is called.
+ * until {@see #onFeatureReady()} is called.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int STATE_READY = 2;
/**
@@ -155,11 +167,15 @@
/**
* The capability was unable to be changed.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int CAPABILITY_ERROR_GENERIC = -1;
/**
* The capability was able to be changed.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int CAPABILITY_SUCCESS = 0;
/**
@@ -331,7 +347,9 @@
*
* @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the
* subscription IDs associated with this slot.
+ * @hide
*/
+ @SystemApi @TestApi
public final int getSlotIndex() {
return mSlotId;
}
@@ -339,7 +357,9 @@
/**
* @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)}
* or {@link #STATE_UNAVAILABLE} if it has not been updated yet.
+ * @hide
*/
+ @SystemApi @TestApi
public @ImsState int getFeatureState() {
synchronized (mLock) {
return mState;
@@ -351,7 +371,9 @@
* stop communication, depending on the state sent.
* @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE},
* {@link #STATE_INITIALIZING}, or {@link #STATE_READY}.
+ * @hide
*/
+ @SystemApi @TestApi
public final void setFeatureState(@ImsState int state) {
synchronized (mLock) {
if (mState != state) {
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 56c8771..0d5a979 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -52,14 +52,18 @@
*
* Any class wishing to use MmTelFeature should extend this class and implement all methods that the
* service supports.
- * @hide
*/
-@SystemApi
-@TestApi
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
+ /**
+ * @hide
+ */
+ @SystemApi @TestApi
+ public MmTelFeature() {
+ }
+
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
@@ -215,11 +219,11 @@
* {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
*
* The capabilities of this MmTelFeature will be set by the framework and can be queried with
- * {@link #queryCapabilityStatus()}.
+ * {@see #queryCapabilityStatus()}.
*
* This MmTelFeature can then return the status of each of these capabilities (enabled or not)
- * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
- * status can also be queried using {@link #queryCapabilityStatus()}.
+ * by sending a {@see #notifyCapabilitiesStatusChanged} callback to the framework. The current
+ * status can also be queried using {@see #queryCapabilityStatus()}.
* @see #isCapable(int)
*/
public static class MmTelCapabilities extends Capabilities {
@@ -228,13 +232,18 @@
* Create a new empty {@link MmTelCapabilities} instance.
* @see #addCapabilities(int)
* @see #removeCapabilities(int)
+ * @hide
*/
+ @SystemApi @TestApi
public MmTelCapabilities() {
super();
}
- /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/
+ /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.
+ * @hide
+ */
@Deprecated
+ @SystemApi @TestApi
public MmTelCapabilities(Capabilities c) {
mCapabilities = c.mCapabilities;
}
@@ -243,11 +252,17 @@
* Create a new {link @MmTelCapabilities} instance with the provided capabilities.
* @param capabilities The capabilities that are supported for MmTel in the form of a
* bitfield.
+ * @hide
*/
+ @SystemApi @TestApi
public MmTelCapabilities(@MmTelCapability int capabilities) {
super(capabilities);
}
+ /**
+ * @hide
+ */
+ @SystemApi @TestApi
@IntDef(flag = true,
value = {
CAPABILITY_TYPE_VOICE,
@@ -278,23 +293,39 @@
*/
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
+ /**
+ * @hide
+ */
@Override
+ @SystemApi @TestApi
public final void addCapabilities(@MmTelCapability int capabilities) {
super.addCapabilities(capabilities);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi @TestApi
public final void removeCapabilities(@MmTelCapability int capability) {
super.removeCapabilities(capability);
}
+ /**
+ * @hide
+ */
@Override
+ @SystemApi @TestApi
public final boolean isCapable(@MmTelCapability int capabilities) {
return super.isCapable(capabilities);
}
+ /**
+ * @hide
+ */
@NonNull
@Override
+ @SystemApi @TestApi
public String toString() {
StringBuilder builder = new StringBuilder("MmTel Capabilities - [");
builder.append("Voice: ");
@@ -319,8 +350,10 @@
/**
* Called when the IMS provider receives an incoming call.
* @param c The {@link ImsCallSession} associated with the new call.
+ * @hide
*/
@Override
+ @SystemApi @TestApi
public void onIncomingCall(IImsCallSession c, Bundle extras) {
}
@@ -329,8 +362,10 @@
* Called when the IMS provider implicitly rejects an incoming call during setup.
* @param callProfile An {@link ImsCallProfile} with the call details.
* @param reason The {@link ImsReasonInfo} reason for call rejection.
+ * @hide
*/
@Override
+ @SystemApi @TestApi
public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) {
}
@@ -338,8 +373,10 @@
/**
* Updates the Listener when the voice message count for IMS has changed.
* @param count an integer representing the new message count.
+ * @hide
*/
@Override
+ @SystemApi @TestApi
public void onVoiceMessageCountUpdate(int count) {
}
@@ -348,14 +385,22 @@
/**
* To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the
* outgoing call as IMS.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int PROCESS_CALL_IMS = 0;
/**
* To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should
* not process the outgoing call as IMS and should instead use circuit switch.
+ * @hide
*/
+ @SystemApi @TestApi
public static final int PROCESS_CALL_CSFB = 1;
+ /**
+ * @hide
+ */
+ @SystemApi @TestApi
@IntDef(flag = true,
value = {
PROCESS_CALL_IMS,
@@ -368,7 +413,9 @@
* If the flag is present and true, it indicates that the incoming call is for USSD.
* <p>
* This is an optional boolean flag.
+ * @hide
*/
+ @SystemApi @TestApi
public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD";
/**
@@ -379,7 +426,9 @@
* certain situations.
* <p>
* This is an optional boolean flag.
+ * @hide
*/
+ @SystemApi @TestApi
public static final String EXTRA_IS_UNKNOWN_CALL =
"android.telephony.ims.feature.extra.IS_UNKNOWN_CALL";
@@ -388,6 +437,7 @@
/**
* @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
* notifies the framework.
+ * @hide
*/
private void setListener(IImsMmTelListener listener) {
synchronized (mLock) {
@@ -406,9 +456,11 @@
* Should be a subset of the capabilities that are enabled by the framework in
* {@link #changeEnabledCapabilities}.
* @return A copy of the current MmTelFeature capability status.
+ * @hide
*/
@Override
- public final MmTelCapabilities queryCapabilityStatus() {
+ @SystemApi @TestApi
+ public @NonNull final MmTelCapabilities queryCapabilityStatus() {
return new MmTelCapabilities(super.queryCapabilityStatus());
}
@@ -420,7 +472,9 @@
* the status of that capability is disabled. This can happen if the network does not currently
* support the capability that is enabled. A capability that is disabled by the framework (via
* {@link #changeEnabledCapabilities}) should also show the status as disabled.
+ * @hide
*/
+ @SystemApi @TestApi
public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) {
if (c == null) {
throw new IllegalArgumentException("MmTelCapabilities must be non-null!");
@@ -433,7 +487,9 @@
* @param c The {@link ImsCallSessionImplBase} of the new incoming call.
* @param extras A bundle containing extra parameters related to the call. See
* {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above.
+ * @hide
*/
+ @SystemApi @TestApi
public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c,
@NonNull Bundle extras) {
if (c == null || extras == null) {
@@ -458,7 +514,9 @@
* @param callProfile The {@link ImsCallProfile} IMS call profile with details.
* This can be null if no call information is available for the rejected call.
* @param reason The {@link ImsReasonInfo} call rejection reason.
+ * * @hide
*/
+ @SystemApi @TestApi
public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile,
@NonNull ImsReasonInfo reason) {
if (callProfile == null || reason == null) {
@@ -497,7 +555,9 @@
/**
* Notify the framework of a change in the Voice Message count.
* @link count the new Voice Message count.
+ * @hide
*/
+ @SystemApi @TestApi
public final void notifyVoiceMessageCountUpdate(int count) {
synchronized (mLock) {
if (mListener == null) {
@@ -518,8 +578,10 @@
* status for capability A.
* @param capability The capability that we are querying the configuration for.
* @return true if the capability is enabled, false otherwise.
+ * @hide
*/
@Override
+ @SystemApi @TestApi
public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
// Base implementation - Override to provide functionality
@@ -537,8 +599,10 @@
* Enabling/Disabling a capability here indicates that the capability should be registered or
* deregistered (depending on the capability change) and become available or unavailable to
* the framework.
+ * * @hide
*/
@Override
+ @SystemApi @TestApi
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@NonNull CapabilityCallbackProxy c) {
// Base implementation, no-op
@@ -561,7 +625,9 @@
* {@link ImsCallProfile#CALL_TYPE_VS_TX}
* {@link ImsCallProfile#CALL_TYPE_VS_RX}
* @return a {@link ImsCallProfile} object
+ * @hide
*/
+ @SystemApi @TestApi
public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) {
// Base Implementation - Should be overridden
return null;
@@ -582,7 +648,9 @@
* {@link ImsCallSession} directly.
*
* @param profile a call profile to make the call
+ * @hide
*/
+ @SystemApi @TestApi
public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) {
// Base Implementation - Should be overridden
return null;
@@ -599,7 +667,9 @@
* call as a conference.
* @return a {@link ProcessCallResult} to the framework, which will be used to determine if the
* call will be placed over IMS or via CSFB.
+ * @hide
*/
+ @SystemApi @TestApi
public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) {
return PROCESS_CALL_IMS;
}
@@ -632,7 +702,9 @@
/**
* @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service
* configuration.
+ * @hide
*/
+ @SystemApi @TestApi
public @NonNull ImsUtImplBase getUt() {
// Base Implementation - Should be overridden
return new ImsUtImplBase();
@@ -641,7 +713,9 @@
/**
* @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE
* calls that support it.
+ * @hide
*/
+ @SystemApi @TestApi
public @NonNull ImsEcbmImplBase getEcbm() {
// Base Implementation - Should be overridden
return new ImsEcbmImplBase();
@@ -650,7 +724,9 @@
/**
* @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event
* package processing for multi-endpoint.
+ * @hide
*/
+ @SystemApi @TestApi
public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() {
// Base Implementation - Should be overridden
return new ImsMultiEndpointImplBase();
@@ -676,7 +752,9 @@
* // Remote side is dead
* }
* }
+ * @hide
*/
+ @SystemApi @TestApi
public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) {
// Base Implementation - Should be overridden
}
@@ -710,7 +788,9 @@
*
* @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS
* Provider.
+ * @hide
*/
+ @SystemApi @TestApi
public @NonNull ImsSmsImplBase getSmsImplementation() {
return new ImsSmsImplBase();
}
@@ -719,14 +799,22 @@
return getSmsImplementation().getSmsFormat();
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi @TestApi
public void onFeatureRemoved() {
// Base Implementation - Should be overridden
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
+ @SystemApi @TestApi
public void onFeatureReady() {
// Base Implementation - Should be overridden
}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 18474a8..0bf64b9 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -222,7 +222,7 @@
@Override
public Network getNetwork() {
- return new Network(mNetworkAgent.netId);
+ return mNetworkAgent.network;
}
public void expectPreventReconnectReceived(long timeoutMs) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b2d363e..1901a1d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -575,7 +575,7 @@
}
};
- assertEquals(na.netId, nmNetworkCaptor.getValue().netId);
+ assertEquals(na.network.netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor);